Agent guide

Copy this entire prompt into Cursor, Claude, or any coding agent to bootstrap a XenSMS integration without hunting through Swagger.

llms.txt
# XenSMS integration guide (for AI coding agents)

You are integrating **XenSMS** — a bulk SMS API for Kenya and beyond.

## Base URL
`https://api.xensms.co/api/v1`

## Auth (programmatic / server-side)
Use **API key + HMAC signature** on message routes. Do NOT use browser JWT for server integrations.

Required headers on `POST /messages/sms` and `POST /messages/bulk/sms`:
- `X-API-Key`: user's key (starts with `xensms_`)
- `X-Timestamp`: Unix ms string
- `X-Nonce`: random 32+ char hex (unique per request)
- `X-Signature`: HMAC-SHA256 hex of the signature string below
- `Content-Type`: application/json
- Use a non-browser User-Agent (e.g. `MyApp/1.0`)

Signature string (note trailing newline):
```
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY_JSON\n
```
- PATH is router-relative: `/messages/sms` or `/messages/bulk/sms` (no /api/v1 prefix)
- BODY_JSON: JSON with recursively sorted object keys

## Send single SMS
```http
POST https://api.xensms.co/api/v1/messages/sms
```
Body:
```json
{
  "recipient": "+254712345678",
  "content": "Hello from XenSMS",
  "senderId": "XENDATA"
}
```

## Send bulk SMS
```http
POST https://api.xensms.co/api/v1/messages/bulk/sms
```
Body:
```json
{
  "recipients": ["+254712345678", "+254700000001"],
  "content": "Your OTP is 1234",
  "senderId": "XENDATA"
}
```

## Balance
```http
GET https://api.xensms.co/api/v1/messages/balance
```
Headers: `X-API-Key` only (no signature on balance).

## Dashboard / human flows
- Sign up: https://app.xensms.co/register
- API keys UI: https://app.xensms.co/api-keys
- Top up credits: https://app.xensms.co/payments (USDT on Celo / USDC on Base)

## Errors
- 401: bad key or signature / expired timestamp / nonce replay
- 402: insufficient credits
- 429: rate limit — backoff and retry

## Node.js signature sketch
```javascript
const crypto = require('crypto');
function sign(method, path, ts, nonce, body, apiKey) {
  const sorted = JSON.stringify(sortKeys(body));
  const str = `${method}\n${path}\n${ts}\n${nonce}\n${sorted}\n`;
  return crypto.createHmac('sha256', apiKey).update(str).digest('hex');
}
```

Implement robust error handling, never log full API keys, and validate E.164 phone numbers before send.