246 lines
4.2 KiB
Markdown
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`.
|
|
|