# 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 ` 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` 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 ` 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 ` returns a `ProblemDetail` response with `status=401`.