Carrier Webhook Retry Windows, By The Numbers

Published retry counts, backoff windows, and OAuth token TTLs for FedEx, DHL, Shippo, AfterShip and more, sourced and dated as of August 2026.

Carrier Webhook Retry Windows, By The Numbers

If you have ever sized a dead-letter queue by guesswork, this is the post that replaces the guesswork with the actual numbers carriers publish. A carrier webhook retry policy and an OAuth access-token lifetime are not documentation trivia. They are inputs to your idempotency window, your DLQ retention, and your token-refresh scheduler. Get the number wrong and you either drop legitimate retries into an already-closed idempotency window, or your refresh job wakes up an hour too late and starts throwing 401s across a fleet of tenants.

Why retry windows and token TTLs are architecture inputs, not trivia

Every retry schedule a carrier publishes implies a minimum lifetime for your deduplication key. Every OAuth token TTL implies a maximum interval before your refresh job must fire. If your idempotency window is shorter than the carrier's longest retry gap, you will process the same label event twice and call it a duplicate-shipment bug when it is really a config mismatch. This post puts the two data sets next to each other: webhook retry policy and OAuth token lifetimes, across the carrier and multi-carrier platforms that publish them, and marks the ones that don't.

Method

Every figure below was read directly from the provider's current developer documentation between late July and 3 August 2026. No sandbox testing, no third-party aggregator numbers taken at face value, no numbers inferred from behaviour. Where a page did not state a figure, the row says "Not published". Ocean and LTL freight APIs, EDI-based carrier integrations, and sandbox-only behaviour are out of scope. Treat every number here as accurate as of 3 August 2026 and re-check before you hardcode it, because carriers change these values without much warning, as the UPS section below shows.

Webhook retry policy, by provider

ProviderRetry attemptsBackoff / intervalAck windowAuto-deactivationSource
FedEx (Advanced Integrated Visibility)3 retries5-minute intervalsNot publishedNot publishedFedEx Developer Portal
DHL eCommerce Americas2 retries (3 total attempts)1 hour, then 6 hours5 seconds, HTTP 20010,000 consecutive failuresDHL API Developer Portal
DHL Shipment Tracking Unified Push2 retries (3 total attempts)1 hour, then 6 hoursNot publishedDeactivation after final failed attemptDHL API Developer Portal
Shippo2 retriesNot published (immediate, on qualifying codes only)3 seconds, 2XXNot publishedShippo Docs
AfterShip14 attempts2^n × 30s exponentialNot published (2XX required)Event discarded after final attempt, no DLQAfterShip Docs
UPSNot publishedNot publishedNot publishedNot publishedNo public webhook retry spec found
USPSNot publishedNot publishedNot publishedNot publishedNo public webhook retry spec found
CargosonNot publishedNot publishedNot publishedNot publishedNo public webhook retry spec found

FedEx will hold the data and attempt to resend it within a span of 3 retries within 5-minute intervals, and if FedEx does not receive a successful response in the defined time span, then FedEx will stop redelivery of that specific event. A separate FedEx product, Shipment Visibility Webhook, uses a different window entirely: FedEx will try to resend track event data to your servers within a 6-hour time window, in case of missed acknowledgement. Two FedEx webhook products, two different retry envelopes. Check which one you're actually subscribed to before you size anything against it.

DHL's eCommerce Americas tracking webhooks give the most explicit schedule of any carrier here. The receiving system must reply to the event POST request with a HTTP 200 response within 5 seconds; if either requirement is not met, the system will retry delivery in 1 hour, and if the second delivery fails, the system will retry delivery 6 hours after the second attempt, after which it will not try again. If event delivery fails 10,000 times consecutively for a specific webhook, that webhook will be automatically deactivated, with the technical contact notified via email. That 10,000-failure threshold matters more than it looks: at high shipment volumes with a broken endpoint, you can burn through it in hours, not weeks.

Shippo's current docs are narrower and more conservative than older versions floating around in blog posts and GitHub gists. Shippo expects your webhook endpoint to return a 2XX HTTP status code in a timely manner, and if it does not process the payload in a timely manner or returns a 408, 429, or 5XX status code, Shippo will retry twice; no retry is attempted upon other client error status codes. Older documentation and third-party mirrors describe a 5-retry, 10-second-interval schedule with disablement on failure, but that language has since been superseded on Shippo's own docs site, so trust the current page over anything cached.

AfterShip publishes the most granular schedule of the bunch. In case of an unsuccessful event, AfterShip Tracking attempts to deliver your webhooks for up to 14 times with exponential backoff, using the formula 2^(number of retry) × 30 seconds. There is no built-in dead-letter queue on their side, so once the 14 attempts are exhausted, the event is gone from AfterShip's pipeline for good, and reconciliation is on you.

What the spread means for your DLQ and idempotency design

DHL's schedule means a full retry cycle spans just over 7 hours from first failure to final attempt. If your idempotency key TTL or your DLQ retention is shorter than that, you will treat DHL's second or third attempt as a fresh event instead of a retry, and duplicate work follows. FedEx's Advanced Integrated Visibility webhook, by contrast, gives up inside 15 minutes. A DLQ timer tuned to DHL's 7-hour cycle will happily hold a dead FedEx subscription for hours after FedEx itself has stopped trying, giving your ops team a false sense that recovery is still possible. AfterShip's 14-attempt curve sits somewhere in between in attempt count but compresses badly at the tail: because there's no DLQ on AfterShip's side, any fallback design has to poll rather than wait, which is a real argument in the webhooks-versus-polling debate for tracking data specifically.

Multi-carrier abstraction layers exist partly to smooth over exactly this kind of inconsistency. Platforms like Cargoson, alongside nShift, EasyPost, and ShipEngine, sit in front of these carrier-specific retry schedules so integrators don't have to hand-tune a DLQ per carrier. That doesn't remove the need to understand the underlying schedules, though, since the abstraction layer still has to absorb the worst case somewhere.

OAuth access-token lifetimes, by provider

ProviderAccess token TTLRefresh token TTLNotesSource
FedEx (core APIs)3,600s (1 hour)Not publishedClient credentials or CSP flowFedEx Developer Portal
FedEx Supply Chain API3,600s (1 hour)2,682,000s (31 days)Capped at 4,096 refresh uses per appFedEx Supply Chain Developer Production
UPS3,600s (1 hour), effective 1 April 2026Not publishedReduced from a 4-hour tokenPitney Bowes Support
DHL eCommerce Americas3,600s (60 minutes)Not publishedRefresh recommended every 15–30 minDHL API Developer Portal
DHL eCommerce (Fulfillment/GAPI, global)18,000s (5 hours default)Not publishedSame token reusable across requests until expiryDHL eCommerce API docs
USPSNot publishedNot publishedNo public token TTL found

The access token typically expires in 1 hour, the refresh token expires in 31 days (2,682,000 seconds), and the maximum number of times a refresh token can be requested is set to 4,096 per application. DHL's eCommerce Americas authentication API states plainly that this limit is currently set to 60 minutes, and DHL recommends refreshing the access token periodically, roughly every 15 to 30 minutes. Its separate global Fulfillment/GAPI authentication endpoint returns a longer-lived token: each obtained token is valid for a certain amount of time, with a default of 5 hours, so you don't have to obtain a token for every API resource request. Two DHL products, two different token lifetimes, same trap as the FedEx webhook example above: know exactly which endpoint you're calling.

The UPS token-lifetime change is a live trap

This is the one to check right now if you integrate with UPS. Starting April 1, 2026, the lifespan of OAuth tokens will change from 4 hours to 1 hour. If your scheduler still refreshes on a hardcoded 4-hour cycle, tokens will start expiring mid-cycle and calls will fail with 401s, intermittently at first and then constantly as the old assumption drifts further from reality. The fix isn't complicated: read `expires_in` from the token response and schedule your refresh against that value, with a safety margin, rather than a constant baked into config. Pair it with reactive refresh on 401 as a second line of defence, so a missed scheduled refresh doesn't take down a shipping lane for an hour before anyone notices.

What "Not published" should trigger in your own monitoring

UPS and USPS not publishing retry-count guarantees isn't a documentation gap you can wait out. It's a signal to build your own synthetic webhook monitor and log what you actually observe, timestamp and status code per attempt, so you have a real table to work from even when the vendor doesn't give you one. This is the same discipline that underlies good observability for shipping APIs generally: if the carrier won't tell you the retry schedule, your own logs become the source of truth until they do. Platforms like Cargoson, nShift, EasyPost, ShipEngine, Sendcloud, and ClickPost all sit in front of these same carrier inconsistencies, and none of them can fully paper over a carrier that doesn't publish a number.

Takeaways

  • Size DLQ retention and idempotency key TTLs to the longest published retry cycle among the carriers you integrate with (DHL's roughly 7-hour cycle, not FedEx's 15-minute one), not the shortest.
  • Read `expires_in` dynamically from every OAuth response. Never hardcode a token lifetime, especially for UPS after 1 April 2026.
  • Add reactive refresh on 401 as a backstop to scheduled refresh, so a missed cron job doesn't silently take down a carrier connection.
  • Where a carrier marks retry policy or token TTL as "Not published" in your own audit, instrument a synthetic monitor and treat your measured data as authoritative until the vendor documents otherwise.