Files
sms-client-portal/API.md
Kwesi Banson Jnr c68c007945 Initial commit
2026-03-19 11:03:33 +00:00

246 lines
4.2 KiB
Markdown

# SMS Gateway API
All endpoints are JSON over HTTP.
## Base URL
- Local: `http://localhost:8080`
## Authentication (Send SMS)
`POST /api/sms/send` requires:
- `Content-Type: application/json`
- `Authorization: Bearer <API Key>`
The API key is generated when creating an application and stored in `applications.api_key`.
## Pagination
For list endpoints:
- `page` (default `0`)
- `size` (default `20`, max `200`)
- `sort` (default `createdAt,desc`) format: `field,asc|desc`
Spring returns a `Page<T>` JSON with fields like `content`, `totalElements`, `totalPages`, etc.
---
## Clients
### Create client
`POST /api/clients`
Request:
```json
{
"name": "Acme Corp",
"email": "contact@acme.com",
"country": "Kenya",
"phoneNumber": "+254700000000"
}
```
Response `201`:
```json
{
"id": 1,
"name": "Acme Corp",
"email": "contact@acme.com",
"country": "Kenya",
"phoneNumber": "+254700000000",
"status": "NEW",
"createdAt": "2026-03-17T06:00:00Z",
"updatedAt": "2026-03-17T06:00:00Z"
}
```
### List clients (paginated)
`GET /api/clients?page=0&size=20&sort=createdAt,desc`
### Get client by id
`GET /api/clients/{id}`
### Update client status (NEW -> ACTIVE/INACTIVE)
`PATCH /api/clients/{id}/status`
Headers:
- `Content-Type: application/json`
Request:
```json
{
"status": "ACTIVE"
}
```
Notes:
- Only accepts `ACTIVE` or `INACTIVE`.
---
## Applications
### Create application (generates API key)
`POST /api/applications`
Request:
```json
{
"name": "My SMS App",
"type": "LOCAL",
"orgId": 1
}
```
Response `201` (includes `apiKey`):
```json
{
"id": 1,
"name": "My SMS App",
"type": "LOCAL",
"orgId": 1,
"apiKey": "sms_....",
"createdAt": "2026-03-17T06:00:00Z",
"updatedAt": "2026-03-17T06:00:00Z"
}
```
### List applications (paginated)
`GET /api/applications?page=0&size=20&sort=createdAt,desc`
Note: list responses do **not** include `apiKey`.
### List applications by client id (paginated)
`GET /api/applications/client/{clientId}?page=0&size=20&sort=createdAt,desc`
### Get application by id
`GET /api/applications/{id}`
---
## Send SMS
### Send SMS (queue message)
`POST /api/sms/send`
Headers:
- `Content-Type: application/json`
- `Authorization: Bearer <API Key>`
Request:
```json
{
"from": "SENDER_ID",
"to": "2547XXXXXXXX",
"refId": "client-app-ref-12345",
"message": "Hello"
}
```
Notes:
- `refId` is the **client reference id** stored in `messages.client_reference_id`.
- The backend generates `messageRefId` (UUID) stored in `messages.internal_reference_id`.
- When sending to UCM, we submit `refId = messages.internal_reference_id` (our UUID) for end-to-end tracking.
- The poller later sends NEW messages to UCM and updates `messages.ucm_message_id` using UCM `msgId`.
Response `201`:
```json
{
"status": "NEW",
"to": "2547XXXXXXXX",
"messageRefId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created_at": "2026-03-17 09:33:00"
}
```
---
## UCM Callbacks
### Delivery receipts callback (configured on UCM)
`POST /api/ucm/delivery-receipts`
Headers:
- `Content-Type: application/json`
Payload:
```json
{
"msgId": "tr-29dd2622-c1c4-46f1-93cb-e40452471924",
"from": "2547XXXXXXXX",
"to": "TIARA",
"refId": "TIARA",
"status": "DeliveredToTerminal",
"statusReason": "DeliveredToTerminal",
"deliveryTime": "2019-02-05 18:27:39.878"
}
```
How it updates `messages`:
- Looks up by: `messages.ucm_message_id == msgId`
- Updates:
- `messages.ucm_delivery_status = status`
- `messages.delivery_time = deliveryTime`
- If `status == DeliveredToTerminal`: `messages.delivery_status = DELIVERED`
---
## Messages
### List messages (paginated)
`GET /api/messages?page=0&size=20&sort=createdAt,desc`
### List messages by client id (paginated)
`GET /api/messages/client/{clientId}?page=0&size=20&sort=createdAt,desc`
---
## Error format
### Validation errors (400)
```json
{
"type": "about:blank",
"title": "Validation Failed",
"status": 400,
"detail": "Request validation failed",
"errors": [
{ "field": "to", "message": "Recipient (to) is required" }
]
}
```
### Auth errors (401)
Missing or invalid `Authorization: Bearer <API Key>` returns a `ProblemDetail` response with `status=401`.