Idempotency and retries
Networks fail. If a send times out you cannot tell whether we received it, and sending it again could text your customer twice. An Idempotency-Key header makes a retry safe.
Send a key with every POST
POST /v1/sms/messages HTTP/1.1Authorization: Bearer 500c_live_…Content-Type: application/jsonIdempotency-Key: order-78123-otpA key is 1 to 64 characters from A-Z, a-z, 0-9, _ and -. Use a value from your own records, such as an order ID, so a retry after a crash uses the same key. Every POST takes one, and every send should carry one.
What a retry gets
Within 24 hours of the first request finishing, a request with the same key gets:
| You send | You get |
|---|---|
| The same body | The first response again, byte for byte, with Idempotent-Replayed: true. Nothing is sent or charged twice |
| A different body | 409 idempotency_key_reused |
| The same body while the first request is still running | 409 conflict: wait a second, then retry |
A key belongs to one endpoint: the same key on /v1/sms/messages and on /v1/sms/messages/bulk counts as two keys, and a cancel's key belongs to that message's cancel. The deprecated POST /v1/messages shares the keys of POST /v1/sms/messages, so a retry can move from one path to the other.
Only successes are kept
A key keeps its response only when the request succeeded. When the first request with a key fails, with a 4xx or a 5xx, nothing is kept and the key is free again: fix the request if it was refused, then retry with the same key. A 409 from the table above is different: it refuses only the later request, and the key stays with the first one.
Which errors to retry
| Code | Retry? |
|---|---|
rate_limited (429) | Yes, after the Retry-After seconds |
service_unavailable (503) | Yes, after the Retry-After seconds, with the same key |
internal_error (500) | Yes, with the same key |
conflict (409) while a key is in progress | Yes, after a second |
insufficient_balance (402) | After you top up |
Any other 4xx | No: fix the request first |
Back off between retries: wait 1 second, then 2, then 4 and so on, up to a minute. Retry a timeout with no response the same way, with the same key.