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 exampledelivered, 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
failedorexpired→ 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. Acancelledmessage does not release its key: the request returns the cancelled message. - New key → treated as a fresh request.
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 thebody:
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
Regenerating the key on retry
Regenerating the key on retry
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.
Reusing a key across different sends
Reusing a key across different sends
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.Reusing old keys
Reusing old keys
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.
Getting 409 when you think the payload matches
Getting 409 when you think the payload matches
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.
