IOSOR Learn

Duplicate webhook must not create a second debit

Fail path: retries and replays stay idempotent on prepaid money and inbox — one event ID, one debit row, one inbox line.

At-least-once delivery will retry. A duplicate webhook that posts a second debit or a second inbox line is a money-and-ops incident, not a “harmless ack.” This page is the fail path: retries and replays stay idempotent on prepaid money and inbox — not the API send-idempotency essay and not the inbound SMS retry playbook.

Related: Signature and replay-window gate, Webhook contract before the first send, Debit rows vs delivery status ledger.

IOSOR is white-label prepaid. USD 20 funds a duplicate-event smoke on one consumer; soft review near USD 1,000/month prices “retry = new charge” as recon debt. Clients see white-label event IDs only.

Idempotency is a fail path, not a slogan

Happy path: one signed event, one accept, one debit. Fail path burns trust — timeout, 5xx, provider replay, operator re-push. Store the idempotency key from the Webhook contract before the first send before side effects: ledger, inbox, CRM. Soft USD 1,000/month treats “ACK then invent a new key” as volume debt; USD 20 proves a forced replay never doubles money.

What counts as a duplicate

Signal Treat as duplicate when Safe outcome
Event ID Same ID already accepted in-window ACK; no second debit
Message ID Same message already ledger-linked Reuse row; no new charge
Inbox key Same MO/MT already filed No second inbox line
Outside replay window Stale retry after gate reject Reject; no money/status write
Unknown type Not on the contract event list Drop; no invented success

The Signature and replay-window gate decides authenticity and freshness. This page owns what happens after a valid duplicate: one end state, one money line, one inbox line.

Money must not move twice

A second debit for the same event ID is a bug even if product “still shows delivered.” Finance filters by event or message ID and sees one prepaid row for that UTC window. Partial side effects after ACK — CRM first, ledger later — manufacture double truth. If processing fails after persist, retry the worker on the same key; do not re-accept the HTTP body as a new charge. Soft volume language stays blocked until duplicate smoke shows one ledger line.

Inbox must not double either

Idempotency is not only money. A replayed inbound or delivery event that opens a second inbox thread trains support to chase ghosts and can trigger autoreply loops. Store the inbox key with the same event ID used for debit. Product and finance share reject/duplicate words: Shared status language for product and finance. USD 20 proves one forced replay leaves inbox cardinality unchanged.

Buyer checklist for duplicate-safe webhooks

  1. Idempotency key shape agreed in the contract and stored before side effects?
  2. Duplicate in-window event ID → ACK without second debit?
  3. Same message ID never opens a second prepaid ledger row?

4.

Start with IOSOR

Force one signed replay inside the window on a corridor that already billed. Export the event id next to the ledger id and prove a single debit row plus a single inbox row. If a second debit appears, halt that consumer and refund the extra row — do not net it against later traffic. This gate is replay money, not an E.164 syntax check and not a shipping-copy job.

IOSOR takeaway

A replay is not a new send. One event id writes one debit.

Do: keep signature plus replay window on, then prove one debit after an in-window POST. Don’t: debit every POST, or treat a network retry as a second invoice.

Was this guide helpful?

Related guides