Skip to main content
Network calls fail. Timeouts happen. You retry. Without idempotency, a retried POST /v1/sms could send the same message twice. Robase fixes this with an Idempotency-Key header: the first successful request wins, and subsequent retries with the same key return the original response without doing the work again.

How it works

Pass a unique key per logical send attempt:
curl -X POST https://api.robase.dev/v1/sms \
  -H "Authorization: Bearer rb_live_xxx" \
  -H "Idempotency-Key: order-92413-dispatch-sms" \
  -H "Content-Type: application/json" \
  -d '{"to":"+2348012345678","from":"SHUTTLERS","body":"Your ride arrives in 3 min."}'
Replayed responses include an Idempotent-Replay: true header so you can tell them apart from fresh requests.

Rules

order-92413-dispatch-sms on /v1/sms is distinct from the same key on /v1/emails. And keys from project A never collide with project B.
After 24 hours the cached response is evicted. If you retry later than that, you get a fresh attempt — so keep retries inside a reasonable window.
We only cache 2xx and 4xx responses. 5xx errors are not cached — the retry with the same key triggers a fresh attempt.
For POST /v1/sms/batch, put the per-row key on each element:
{
  "from": "SHUTTLERS",
  "messages": [
    { "to": "+2348012345678", "body": "...", "idempotency_key": "order-1-sms" },
    { "to": "+2348087654321", "body": "...", "idempotency_key": "order-2-sms" }
  ]
}
Per-row keys are namespaced separately from the top-level Idempotency-Key header.

What to use as a key

A good idempotency key is derived from your data, not random:
  • order-92413-dispatch-sms
  • user-42-password-reset-2026-04-17
  • uuid.v4() ❌ (a fresh UUID per retry is not idempotent — you’ll send twice)
If your natural key isn’t unique per send attempt, compose it: user-42-reset-${Date.now()} bucketed to the minute.