# 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`.

