> Part of [WHCC Developer Documentation](https://www.whcc.com/developer/llms.txt)

# Delivery, Retries & Idempotency

WHCC webhook delivery is **at-least-once**, not exactly-once. Your endpoint has to handle two things correctly: responding fast enough to avoid self-inflicted retries, and treating duplicate deliveries as normal rather than exceptional.

> ## Deduplicate on `ConfirmationId` + `SequenceNumber` + event type
>
> Every notification carries a `ConfirmationId` and a `SequenceNumber` &mdash; top-level JSON fields on V2, top-level form fields (also inside the `json` payload) on V1. WHCC may send the same notification more than once, so your handler has to be idempotent. But **the key matters**, and so does what you do when it repeats.
>
> **Use all three parts of the key.** A `ConfirmationId` is *not* unique to one order. When WHCC splits an order across shipments or production paths, the resulting orders share a `ConfirmationId` and are told apart by `SequenceNumber` (for example `"1"` and `"1.2"`) &mdash; each with its own `OrderNumber` and its own tracking. Keying on `ConfirmationId` + event type alone will silently discard the second half of every split order.
>
> **A repeat is not always a no-op.** Even with the full key, a later delivery can carry *more* data than the earlier one &mdash; most commonly a `Shipped` notification re-sent moments later with additional tracking numbers as more packages are scanned. Treat a repeat as a **reconcile**, not a discard: re-apply the payload so the newer content wins, and make the side effects (emails, downstream syncs) fire only on genuinely new information.
>
> Duplicates are structural, not exceptional:
>
>
> None of these are bugs. Key on **(`ConfirmationId`, `SequenceNumber`, event type)**, reconcile rather than drop, and repeats become harmless.

## Response timeout

Your endpoint has **100 seconds** to return a response once WHCC opens the connection. If your endpoint is still processing when the timeout elapses, WHCC treats the request as failed and retries it &mdash; even if your endpoint eventually would have returned `200`. A slow endpoint therefore generates its own retries and its own duplicates.

> Return a `2xx` as soon as you have durably recorded the notification. Do any slow downstream processing (image lookups, order sync, notifications to other systems) asynchronously, after you respond.

## Retry logic

What happens next depends on the response WHCC receives:

| Response | Outcome |
| Any 2xx | Success. Delivery complete; never retried. |
| 301, 302, 303 | The redirect is followed, but replayed as a GET with no body, so your endpoint never receives the notification. If that GET returns a 2xx, WHCC records the delivery as successful and never retries it &mdash; the notification is lost silently. Never register a callback URI that redirects &mdash; including a plain http:// URL that your server upgrades to https://. Register the final destination directly. |
| Any 4xx | Terminal. WHCC stops sending this notification permanently. |
| Any 5xx | Retried. |
| Connection failure (refused, reset, DNS failure) | Retried. |
| No response within 100 seconds | Retried. |

> **Upcoming changes.** Two parts of the table above will change in a future release. No date is set yet, so write your integration to be correct under both the current and the future behaviour.

## Retry schedule

Retries use exponential backoff: the delay before attempt `n` is `6^(n-1)` minutes. These are *minimums* &mdash; a retry becomes eligible after the delay elapses and is sent on the next delivery pass, so the real gap can be slightly longer, particularly at busy times. WHCC currently makes up to **7 attempts** total (the initial attempt plus 6 retries) before giving up.

| Attempt | Minimum delay since previous attempt | Minimum elapsed since first attempt |
| 1 | &mdash; (initial delivery) | 0 |
| 2 | 6 minutes | 6 minutes |
| 3 | 36 minutes | 42 minutes |
| 4 | 216 minutes (3.6 hours) | ~4.3 hours |
| 5 | 1,296 minutes (21.6 hours) | ~1.1 days |
| 6 | 7,776 minutes (5.4 days) | ~6.5 days |
| 7 | 46,656 minutes (32.4 days) | ~38.9 days |

> A single notification can therefore keep retrying for up to **~39 days** after the triggering event, if your endpoint keeps returning a retryable failure. Size your idempotency storage with that window in mind &mdash; a repeat can arrive more than a month after the original. If you expire dedup keys after a week, a delivery on attempt 7 will look brand new.

> **Upcoming change.** That last row is longer than intended. In a future release the cap becomes **6 attempts**, ending the window at **~6.5 days**. Retaining dedup keys for ~39 days is correct under both the current and the future schedule, so size for 39 days now and you will not need to revisit it.

After the final attempt fails, WHCC stops retrying that notification. There is currently no partner-facing endpoint to query undelivered notifications, so a monitored, reliably-responding endpoint is the only way to guarantee delivery.

> ## What's Next
>
> [Security and Signatures](https://www.whcc.com/developer/docs/order-submit-api/webhook-security/index.html.md) describes how to validate that a webhook request actually came from WHCC.