Skip to main content
Sending the same message twice — because of a network retry, a request timeout, or a deploy mid-request — is a real risk in any production integration. iBubbl handles this with required idempotency keys on every send.

How it works

POST /v1/messages requires an idempotency_key field on every request. iBubbl stores the key with the message it created, alongside a hash of the request payload. Keys never expire. When you send with a key that your account has used before:
  • Same key + same payload → iBubbl returns the original message (HTTP 202, the same message ID). The response shows the message’s current state, for example delivered, not the state it had when you first sent it. Nothing is sent again — however long ago the original was sent.
  • Same key + different payload → 409 idempotency_conflict, whatever the original message’s status. This usually means your code generated one key for two genuinely different messages.
  • Same key + same payload, and the original message ended failed or expired → the key is released and the request is sent as a new message, with a new ID. This is the one case where reusing a key sends again, so retrying a failed send with its original key is safe. A cancelled message does not release its key: the request returns the cancelled message.
  • New key → treated as a fresh request.
The key is the only duplicate protection. iBubbl never compares message bodies. A new key with an identical body sends a second, real message, even milliseconds after the first. See Duplicates and retries, including two edge cases: simultaneous requests with one key, and timed-out sends that create a group.

Generating keys

Use a new UUIDv4 for every logical send. Generate it once per message, before the first attempt, and reuse that same value across retries of that one message.

Scoping

Idempotency keys are scoped per customer account. Two different iBubbl accounts can use the same key without collision — but within a single account, the namespace is shared across all API keys for that account. (If you have multiple API keys for the same account, they share the idempotency-key space.)

What “same payload” means

iBubbl hashes the routing field exactly as you sent it, plus the body: The routing field is compared as text, not by the contact it resolves to. So a retry that switches from to to the same contact’s contact_id (or reformats the phone number) is a different payload and gets 409 idempotency_conflict. Retry with the exact request you sent the first time.

Common pitfalls

If you generate the key inside your retry loop, each retry gets a fresh key and the deduplication never fires. Generate it once per message, before the first attempt, and reuse it.
The key represents one logical send. If your code sends to multiple contacts in a campaign, each one needs its own key. A key generated once at module level and shared by every send gets 409 idempotency_conflict on the second message.
Keys never expire, so a key reused months later still replays (or conflicts with) the original message. Never derive keys from something that repeats, such as a date or a contact ID alone.
Any difference in the hashed fields above — including whitespace in body, or a different spelling of the same phone number — triggers a conflict. Compare your retry’s payload character by character against the original.