API ReferenceSMS API
Errors
The error envelope, the X-Request-Id header, and common SMS API error codes.
Error envelope
Every error response carries a structured envelope. The detail field is kept
for backward compatibility, and error gives a stable machine-readable shape:
{
"error": {
"code": "insufficient_balance",
"message": "Insufficient balance. Required USD 0.0192, available USD 0.01",
"request_id": "3f9a1c2b4d5e6f708192a3b4c5d6e7f8",
"details": {
"required": 0.0192,
"available": 0.01,
"segments": 2,
"recipients": 2
}
},
"detail": { "code": "insufficient_balance", "...": "..." }
}| Field | Description |
|---|---|
error.code | Stable code to branch on (see the table below). |
error.message | Human-readable explanation. |
error.request_id | Correlation ID, also returned in the X-Request-Id header. |
error.details | Optional extra context for the error (e.g. required/available). |
X-Request-Id
Every response - success or error - includes an X-Request-Id header. If you
send your own X-Request-Id, it is echoed back; otherwise one is generated.
Include this value when contacting support so a failed call can be matched to a
log line.
X-Request-Id: 3f9a1c2b4d5e6f708192a3b4c5d6e7f8Common error codes
| HTTP | code | When it happens |
|---|---|---|
400 | bad_request | Malformed request - bad phone number, unroutable country, missing/invalid fields. |
401 | unauthorized | Missing, invalid, or expired API key / token. |
402 | payment_required / insufficient_balance | Wallet balance does not cover the send (required, available in details). |
403 | forbidden | Not allowed for this account. |
403 | insufficient_scope | API key lacks the required scope (e.g. send). |
403 | account_suspended | The account is suspended and cannot send. |
403 | recipient_opted_out | The recipient is on your STOP list. |
404 | not_found | Message, batch, template or verification does not exist (or is not yours). |
409 | conflict / duplicate_message | Identical message to the same number within 5 minutes. |
413 | message_too_long | A rendered message exceeds the segment cap (max_segments in details). |
422 | validation_error | Request body failed schema validation (details.errors lists the fields). |
429 | rate_limited / too_many_requests | Per-credential rate limit exceeded (single send 20/second, bulk 10/minute), or too many OTP starts for one number. |
500 | internal_error | Unexpected server error - retry, and quote the request_id if it persists. |
Handling errors
- Branch on
error.code, not onerror.message(messages may change). - Treat
429and5xxas retryable, ideally with backoff; treat4xx(except429) as caller errors to fix. - Log
error.request_id/ theX-Request-Idheader on failures so support can trace them.