↑ ↓ to navigate · ↵ to open
Documentation menu
Getting started
Building calls
Operate
Operate
Rate limits & idempotency
Two different protections, easy to conflate: traffic caps stop you from placing calls faster than your account allows, and idempotency keys stop a retried request from placing a second call by accident.
Traffic caps
Every account has two independent limits on outbound call placement:
| Cap | Default | Refusal |
|---|---|---|
| Concurrency — simultaneous live calls | 50 | 429, reason LIMIT.0429 |
| CPS — new calls started per second | 10 | 429, reason LIMIT.0430 |
Both are per-account, checked before a call is placed — a refused call is never dialled and never billed. If your traffic legitimately needs a higher ceiling, contact support; these are account settings, not hardcoded platform limits.
A cap refusal on POST /calls/originate returns the standard error body:
// HTTP 429
{ "detail": "Concurrency cap reached (50 simultaneous calls)." }
Distinguish "slow down / raise my cap" (LIMIT.*) from "top up your wallet" (BILL.*, 402) — they're never the same code, because the fix is different. See reason codes for the full table.
Handling a 429
- Back off before retrying — an immediate retry against a CPS cap will just hit it again.
- For a concurrency cap, wait for a call to end (watch the event stream for
call.completed/call.failed) rather than polling. - If you're consistently hitting a cap under normal traffic, that's a sign to ask us to raise it, not to build a bigger retry loop around it.
Idempotency
Any endpoint that spends money or places a call accepts an Idempotency-Key header. Send the same key on a retry — after a timeout, a dropped connection, anything — and you get back the original response instead of a second call or a second charge.
curl https://sauti-pbx.services.co.ke/api/calls/originate \
-H "Authorization: Bearer sk_live_..." \
-H "Idempotency-Key: order-4021" \
-d '{ "to": "+254711111111", "from": "+254709080010", "url": "https://yourapp.com/handle-call" }'
Rules
- Scope. Keys are per-account — two accounts can safely reuse the same key.
- Window. A key is remembered for 24 hours from its first use, then it's free to reuse for a new operation.
- In flight. If a request with that key is still being processed, a concurrent duplicate gets
409, not a second call — retry shortly after. - Completed. Once the original request finished, a retry with the same key replays that exact response verbatim (same status, same body) — it does not re-run the operation.
- Failed attempts don't burn the key. If the operation was refused (
402,429, validation error), the key is freed immediately — a legitimate retry after fixing the problem is not blocked. - Pick a key that identifies the business action you're taking (an order id, a job id) — not a random value per HTTP attempt, or you lose the protection.
No key, no protection: without Idempotency-Key, a retried POST /calls/originate after a timeout can place a second call. Always send one on money-moving requests.