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

**Legacy** This page is reference for integrations already built on the Order Submit API. It remains supported. New integrations use the [WHCC API](https://www.whcc.com/developer/docs/quick-start/index.html.md).

# 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:
>
> - WHCC may `POST` your endpoint successfully, then fail to record that the delivery succeeded, and send it again.
> - A delivery interrupted part-way through &mdash; by a restart or a crash &mdash; is re-sent, because from WHCC's side it never completed.
> - Multi-item shipments and repeated voids can produce repeated notifications for the same order upstream, before delivery even begins.
>
> 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.
>
> - `408 Request Timeout` and `429 Too Many Requests` will become **retryable**, rather than terminal like the rest of the `4xx` range. Until then, treat them as terminal &mdash; do not rely on WHCC retrying them.
> - **Redirects will no longer be followed.** A `3xx` will be treated as a failed delivery. If your endpoint currently returns a redirect, point your registered callback URI directly at the final destination now.

## 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/legacy/order-submit-api/webhook-security/index.html.md) describes how to validate that a webhook request actually came from WHCC.