Error philosophy
Errors in the API are designed to be immediately actionable — every failure response tells you exactly what went wrong and what to do next. Three principles guide the design: 1. The error code is stable, the message is human. Every error body contains a machine-readableerror code that is stable across API versions. Use this in your conditionals. The message field is plain English, safe to log or display, but not for programmatic branching.
2. The status code maps to a category, the body maps to a cause.
402 always means insufficient balance. 429 always means you’re rate-limited. 422 means the request was syntactically valid but semantically wrong. The body narrows it further with a specific error code and details where relevant.
3. Opaque errors are a bug.
A 403 that gives you nothing to act on, or a 500 with no context, is a platform defect. Every error response should either tell you how to fix the request or tell you to retry with specific timing guidance.
Billing on success
Successful sends return abilling object inline — no separate call needed to see your balance, hold, tier position, or message classification.
| Field | Meaning |
|---|---|
hold_amount | Funds reserved for this in-flight message |
message_price | Expected final cost once delivery is confirmed |
balance.free | Immediately spendable — what the 402 check runs against |
balance.reserved | Currently held across all in-flight messages |
tier.volume_used | Messages sent this month toward the next tier threshold |
Common HTTP errors
| Status | Meaning | What you get |
|---|---|---|
| 400 | Validation failure | details[] with field-level errors |
| 402 | Insufficient balance | required_amount, topup_url |
| 403 | Policy violation or agent not approved | Agent status or generic test error |
| 404 | Unknown resource or device not RCS-capable | Context fields per route |
| 422 | Semantic validation failure | Domain-specific reason in error |
| 429 | Rate limited | retry_after |
Idempotency
Send endpoints accept an idempotency key so safe retries never duplicate a message. Pass the key in theIdempotency-Key request header. Use a UUID generated per logical send attempt — retry the same request with the same key if it fails.
Related
Errors reference
Full status code table, error envelope shape, and 402 balance response fields.
Billing API
Proactive balance checks and tier queries before large sends.
API conventions
Rate limits, retry patterns, and idempotency details.
Our philosophy
Why errors and billing are designed to be transparent and actionable.