Errors and rate limits: what to retry, what to surface
HTTP status codes Call2Me returns, rate limit headers, retry-after semantics, and the patterns that make integrations survive carrier hiccups.
Updated October 6, 2026
Most production issues don't come from the happy path — they come from how integrations handle the unhappy ones. This is the reference for what the platform returns and how to react.
Status codes you'll see
| Code | Meaning | What to do |
|---|---|---|
| 200 OK | Success | — |
| 201 Created | Resource created | Read Location header for canonical URL |
| 400 Bad Request | Validation failed | Fix the request; don't retry as-is |
| 401 Unauthorized | Missing or bad auth | Check Bearer token; do NOT retry |
| 402 Payment Required | Wallet too low | Top up, then retry |
| 403 Forbidden | Auth okay, scope insufficient | Mint a key with the right scope |
| 404 Not Found | Resource doesn't exist | Don't retry; check the ID |
| 409 Conflict | State conflict (e.g. number already bound) | Inspect; usually no retry |
| 422 Unprocessable | Schema valid but logically wrong | Fix the request |
| 429 Too Many Requests | Rate limited | Back off; respect Retry-After |
| 500 Internal Server Error | Platform side bug | Retry with exponential backoff |
| 502 / 503 / 504 | Upstream issue | Retry with exponential backoff |
Error response shape
Every error has the same body:
{
"error": {
"code": "validation_failed",
"message": "to_number must be in E.164 format",
"field": "to_number",
"request_id": "req_abc123"
}
}
Always log request_id. When you open a support ticket, that's the only
piece of info we need to trace the request through every internal hop.
Rate limits
Limits are per-API-key, sliding window. Each response includes:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1714998000
When you hit zero:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Retry-After is in seconds. Wait that long, then retry. If you're seeing
429 frequently, contact support to discuss raising your limits — for
high-volume customers we tune them per workspace.
Retrying POSTs safely
There is no idempotency header. An earlier version of this page documented one with a 24-hour response cache; it was never built and the section was removed on 6 October 2026.
For POSTs that create something, a blind retry can create it twice — in
particular POST /v1/calls can place a second call and charge for it.
Safe pattern:
- Put your own request id in
metadata({"metadata": {"req": "…"}}). - On timeout or 5xx, query first —
GET /v1/calls?agent_id=…and look for your id inmetadata— and only then retry. - For wallet top-ups the Paddle checkout is the dedupe boundary; never
re-POST
/v1/wallet/topupon a timeout, open the returned URL again.
Retry strategy that works in production
attempt 1 → fail with 502
wait 1s
attempt 2 → fail with 502
wait 2s
attempt 3 → fail with 502
wait 4s
attempt 4 → succeed
Caps to apply:
- max attempts: 5
- max total wait: 30s
- never retry 4xx (except 408, 429)
- always honor
Retry-Afterif present
Most HTTP client libraries have this built in — use the library, don't reinvent it.
What's next
- Authentication — the 401 and 403 root causes
- Webhooks — retry semantics for events you receive
- Pricing — when 402 is right and what to do about it
Frequently asked
Q.Are 5xx errors safe to retry?
Yes — with exponential backoff. 5xx means the platform side failed; idempotent operations (GET, PUT, DELETE) can be retried as-is. For POST that creates something (a call, an agent), check first whether the resource was created — the API has no idempotency header, so a blind retry can place a second call.
Q.How does rate limiting work?
Per-API-key, sliding window. Headers X-RateLimit-Limit and X-RateLimit-Remaining tell you where you stand. When you hit zero, you get 429 with a Retry-After header.
Q.Is there an idempotency key for POST requests?
No. An earlier version of this page described such a header with a 24-hour cache; it was never implemented and was removed on 6 October 2026. Dedupe on your side: keep your own request id in metadata and query before retrying.
Q.Why am I getting 402 Payment Required?
Wallet balance is below the minimum to start a call. Top up via dashboard or POST /v1/wallet/topup, then retry.