Errors
Errors use conventional HTTP status codes and one JSON shape, with a stable machine-readable code you can branch on.
Error shape
Every error response has a JSON body with a human-readable error and a stable code. Validation errors also include details.
400 application/json
{
"error": "Invalid request",
"code": "invalid_request",
"details": [
{ "path": ["to"], "message": "Required", "code": "invalid_type" }
]
}The error text may change. Write your logic against code and the HTTP status.
Error codes
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Validation failed (details lists each problem), bad JSON, a non-Nepal number, or text over 6 segments. |
| 401 | unauthorized | x-api-key missing or invalid. |
| 402 | insufficient_balance | Workspace balance is lower than the message cost. |
| 403 | client_disabled | The API client that owns the key is disabled. |
| 403 | workspace_pending | POST before Lacspace has activated your workspace. |
| 403 | workspace_suspended | POST while the workspace is suspended. |
| 403 | forbidden | The message type is not allowed for your workspace. |
| 404 | not_found | Unknown id, or a message that belongs to another API client. |
| 409 | idempotency_conflict | Idempotency-Key reused with a different body, or the first request is still running. |
| 429 | rate_limited | A rate or OTP limit was hit. Wait for Retry-After seconds. |
| 500 | server_error | Unexpected error on our side. Retry with backoff and the same Idempotency-Key. |
| 503 | unavailable | No sending route is available right now. Nothing was charged. |
| 503 | sms_disabled | The SMS service is temporarily switched off. Retry later. |
Answers that are not errors
A wrong OTP is a normal answer from POST /sms/otp/verify, returned with HTTP 200:
200 application/json
{ "success": true, "verified": false, "reason": "invalid_code" }| reason | Meaning |
|---|---|
invalid_code | The code does not match. The attempt is counted. |
too_many_attempts | 5 wrong attempts were made. Send a new code. |
no_active_otp | No code was sent for this number and purpose, it expired, or it was already used. |
Likewise, a message that ends as undelivered or failed is reported through its status, not as an API error.
Handling errors
| Status | Retry? | What to do |
|---|---|---|
| 400, 403, 404 | No | Fix the request, the key or the workspace setup. |
| 401 | No | Check the key; it may have been rotated. |
| 402 | After top-up | Add balance, then retry with the same Idempotency-Key. |
| 409 | Yes, carefully | If the first request is still running, wait 1 s and retry. Otherwise use a new key for a new message. |
| 429 | Yes | Wait Retry-After seconds. |
| 500, 503 | Yes | Exponential backoff with jitter, same Idempotency-Key. |