Guides

Reliability: idempotency and automatic retries

How the API behaves during outages, duplicates, and replays, and how to write a robust integration.

Updated November 12, 2026View as Markdown

A messaging integration must survive network failures, restarts, and duplicates. Here is what the API does for you, and what is left for you to do.

Send idempotency#

A send is not trivial: replaying it by mistake would send the same message to a customer twice. The Idempotency-Key header prevents that.

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: appointment-2026-11-14-customer-42" \
  -d '{ … }'
SituationResult
First request with this keyThe message is accepted and recorded.
Same key, same requestNo new message: you get the original message back, with the Idempotent-Replayed: true header.
Same key, different request422 idempotency_conflict.
Two simultaneous requests with the same keyOne succeeds, the other is rejected (409): retry.
Real error · HTTP 422
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This idempotency key has already been used for a different request",
    "requestId": "req_af73c415-1365-41e6-801f-5fc18b1ee2ec"
  }
}

Choose a key that describes the business intent (appointment-reminder-<appointment id>), not a random identifier regenerated on each attempt: that is what makes replay safe.

Sending is asynchronous#

202 Accepted means accepted, not delivered. The message is queued, then sent:

  • Temporary Meta or network outage, rate limit: the message is retried automatically, up to 6 attempts with an increasing wait.
  • Permanent rejection (template not found, recipient unreachable…): no pointless retry; the message goes straight to failed, with Meta's code and reason.
  • Throughput: sends are throttled per number (50 messages per second by default): during a spike, they wait their turn without being lost.
  • Restart or queue loss: a message that stays queued for more than 5 minutes is picked up automatically by a maintenance task.

Statuses: never backward#

Meta can deliver its notifications twice or out of order. Bref Point Inbox applies a simple rule: a status only moves forward (queued → sent → delivered → read), and failed does not overwrite a message that is already read. Even a read that arrives before its delivered is accepted, and the missing step is filled in.

Webhooks: at least once#

Each event is delivered at least once, never "exactly once". Your receiver must therefore be idempotent:

  1. Verify the signature.
  2. Store the event id (a table with a uniqueness constraint is enough).
  3. If it already exists, respond 200 without doing anything.
  4. Otherwise, process it, then respond 200.

See Webhooks for the retry schedule and replay.

What's left for you to do#

  • Set a timeout on your calls (10 to 30 seconds) and retry with the same Idempotency-Key on timeout, network error, or 5xx.
  • Don't retry a 4xx error without fixing the request: it will fail the same way.
  • Respond to your webhooks in under 10 seconds; hand long-running work off to a queue on your side.
  • Monitor GET /webhook-deliveries?status=failed and your failed messages.