Techdome
All Insights/System Design

Webhook retries and idempotency keys, together

Webhook retries need stable idempotency keys so repeated deliveries are safe, deduplicated, observable, and recoverable through DLQs.

Techdome Engineering
Techdome EngineeringEngineering Team
Oct 26, 20263 min read
Verified Architecture
Illustration of webhook retry flow using a stable idempotency key, de-duplication store, and dead-letter queue.

Webhook retries and idempotency keys solve different parts of the same reliability problem. Retries make delivery likely after timeouts or non-2xx responses; idempotency keys make repeated delivery safe by letting the receiver identify the same logical event.

Webhook retries and idempotency keys flow

The sender creates an event, assigns a stable delivery identity, and sends it as a header or body field, often something like X-Idempotency-Key. Provider delivery IDs such as GitHub's X-GitHub-Delivery or Shopify's webhook ID can serve this role when they are preserved across redeliveries.

If the receiver returns non-2xx or times out, the sender puts the delivery into a retry queue. A retry worker later replays the same payload with the same key. That preservation is the contract: a new key turns a retry into a new event.

Flow showing a webhook sender retrying the same idempotency key through a retry worker until the receiver de-duplicates or sends exhausted attempts to a DLQ.
Flow showing a webhook sender retrying the same idempotency key through a retry worker until the receiver de-duplicates or sends exhausted attempts to a DLQ.

Receiver-side de-duplication

The receiver should verify the signature and timestamp first, then check a de-duplication store keyed by the idempotency key. Common implementations are a database row with a unique constraint, a Redis set with TTL, or a dedicated idempotency table that records status and result.

For a new key, insert or lock the record, enqueue work, and process side effects. For an existing key, return 200 or the recorded result without repeating the side effect. Downstream calls should also use idempotency keys or upsert semantics, because duplicate prevention at the webhook edge does not automatically protect payment, email, CRM, or provisioning APIs. Tools like Sparrow are useful for exercising duplicate deliveries while testing API behavior.

Trade-offs and when not to use it

Receiver-side de-duplication is usually the safest default because the receiver knows which side effects matter. Content-based de-duplication is weaker: two payloads can look similar but represent different intent, and hashes do not replace a stable delivery ID.

Store keys for longer than the sender's maximum retry window, plus operational margin. A short TTL can re-admit an old retry as new work. A very long TTL costs more storage but gives safer replay handling.

Do not add complex idempotency machinery for disposable analytics events where occasional duplicates are acceptable. Do use it for payments, provisioning, patient workflows, account changes, affiliate attribution, or any automation where repeating an action causes real operational cleanup.

How this looks in practice

In A Multi-Vendor Telehealth Care Ecosystem, the architecture connected payments, intake, scheduling, community, and consultation software through a unified identity and lifecycle model. The build included server-to-server affiliate postbacks and automated community provisioning after checkout completion, both patterns where event identity and replay safety matter.

The anonymised case study reports: Deal to Kickoff: ~2.5 Wks; Follow-on SOW: 13 Days; Vendor Logins: 5 → 1. A related automation pattern appears in Five Rapid Automation Deployments, where webhook and API orchestration supported rapid workflow deployments across operational bottlenecks.

Takeaways

  • Retries improve delivery; idempotency keys prevent duplicate effects.
  • The retry worker must replay the original payload with the original key.
  • The receiver should verify, de-dupe, enqueue, and acknowledge quickly.
  • Exhausted retries need a DLQ or manual-resolution queue that preserves key, payload, and metadata.

Further reading

References

FAQ

Questions readers ask

Should the sender or receiver generate the idempotency key?

Prefer a sender-preserved delivery ID from the provider. If none exists, a sender-generated UUID is fine, but it must stay the same across every retry.

What should happen if processing fails after the receiver returns 200?

The receiver should have already placed the work on a durable internal queue. Internal worker failures should then use their own retry and DLQ path.

How long should we keep idempotency keys?

Keep them at least as long as the sender's retry window, plus margin. Align TTLs with the provider or queue system that performs retries.

Can we skip the DLQ?

Only if loss is acceptable. For business-critical events, a DLQ preserves the original key, payload, and failure context for safe inspection or replay.

Continue reading

More foundry teardowns

View all