Errors
Every error has the same shape and a stable code you can branch on.
JSON
{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Your SMS balance is insufficient."
}
}Branch on error.code, never on error.message — messages get reworded for clarity, codes do not change once published.
Reference
| Code | Status | What to do |
|---|---|---|
VALIDATION_ERROR | 400 | Check error.details.fieldErrors for the offending field. |
INVALID_API_KEY | 401 | Key missing, malformed, revoked, or not matching the org header. |
ACCOUNT_SUSPENDED | 403 | Account suspended, or a live key used before approval. Contact support. |
FORBIDDEN | 403 | Usually the IP allowlist. Add the sending server's address. |
SENDER_NOT_APPROVED | 403 | Wait for approval, or use a different approved sender ID. |
INVALID_SENDER | 400 | Register the sender ID on your account first. |
INSUFFICIENT_BALANCE | 402 | Not enough SMS credits for the send — buy a bundle. Wallet money is never charged per message, so a funded wallet does not prevent this. Also returned when you try to buy a bundle your wallet cannot afford; the message says which. |
INVALID_PHONE_NUMBER | 400 | No valid recipients. Check rejected_recipients for why. |
RECIPIENT_SUPPRESSED | 400 | Every recipient opted out. Use a transactional type if appropriate. |
INVALID_MESSAGE | 400 | Body empty or over 10 segments. |
DUPLICATE_REQUEST | 422 | Idempotency key reused with a different body. Use a new key. |
REQUEST_IN_PROGRESS | 409 | The same key is still processing. Retry in a moment. |
RATE_LIMITED | 429 | Back off and retry. The message says how long. |
PROVIDER_ERROR | 502 | Upstream network problem. Safe to retry with the same idempotency key. |
NOT_FOUND | 404 | The resource does not exist, or belongs to another account. |
INTERNAL | 500 | Our fault. Retry; if it persists, contact support with the time. |
Validation detail
A VALIDATION_ERROR names the fields that failed, so you can surface the problem rather than a generic message.
JSON
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request",
"details": {
"formErrors": [],
"fieldErrors": {
"sender": ["Sender ID cannot exceed 11 characters"],
"recipients": ["At least one recipient is required"]
}
}
}
}What to retry
| Status | Retry? |
|---|---|
| 400, 403, 404, 422 | No — the request will never succeed unchanged. |
| 401 | No — fix the credentials first. |
| 402 | After topping up. |
| 409, 429 | Yes, after a delay. |
| 500, 502, 503 | Yes, with backoff and the same idempotency key. |