Purchases & balance · WebhooksDelivery, Retry & Dedup

Delivery, Retry & Dedup

What your endpoint must do

  1. Verify the signature over the raw request body and check timestamp freshness before trusting or deduplicating the request.
  2. Durably accept a new event in an inbox or queue keyed by event_id. Commit that write before returning 2xx, ideally within ~10 seconds. If storage fails, return 503 so delivery can be retried.
  3. For a valid event already durably accepted, return 2xx again. Do not repeat its business effect, and do not return 4xx merely because its ID is familiar.
  4. Process the inbox asynchronously. Commit each business effect and its dedup key together, or use an equivalent idempotent operation. Retry local processing until it succeeds; TradeOn stops sending after your 2xx.
  5. Tolerate unknown fields and valid, signed unknown event types. Record or ignore unknown types and return 2xx (versioning).

A process-local set of IDs is not a durable inbox. It loses protection on restart and can lose an event if you mark it processed before its work is safely stored.

Retry policy

The delivery log exposes an attempt counter, delivery_attempts. The outcome of an attempt determines the next action:

OutcomeWhat triggers itWhat happens next
SuccessHTTP 2xxMarked delivered. No further automatic attempts.
TransientHTTP 408, 429, any 5xx; network or DNS failure; 30-second attempt timeoutRetried while automatic attempts remain.
TerminalOther HTTP 4xx; a blocked destination; missing URL or secret; a permanent payload errorMarked failed. No automatic retry.

The automatic backoff is 1 minute → 10 minutes → 1 hour. There are at most three retries after the first attempt. Delays start from the latest failed attempt, not from event creation; these are retry delays, not a delivery-time guarantee.

attempt #1 fails transiently → attempt #2 after 1 minute
attempt #2 fails transiently → attempt #3 after 10 minutes
attempt #3 fails transiently → attempt #4 after 1 hour
attempt #4 fails transiently → failed; no more automatic attempts

A 4xx other than 408/429 stops the chain immediately. Correct your endpoint or URL, then use the manual replay procedure for failed, stored events.

Per-attempt timeout

Each attempt has a 30-second wall-clock limit, including DNS, TLS, the request, and reading the response body. Aim to acknowledge durable intake within ~10 seconds so there is room for network delays. Put lengthy processing behind your inbox.

Dedup contract

Keep two separate deduplication records:

LayerKeyPurpose
Delivery intakeevent_id (also X-Event-Id)Accept one stored event once, despite repeated delivery or manual replay.
Purchase business effect(data.purchase_id, event) for every purchase.* typeApply that effect once to that purchase attempt, including late rollbacks.
Deposit business effectdata.provider_uuid for balance.depositedCredit a deposit once even if two stored events have different event IDs.

Scope these keys by merchant if your receiver serves multiple merchant accounts. Do not deduplicate purchases on custom_id alone: buy-by-name can produce multiple attempts, and a completed purchase can later receive a distinct rollback event.

For example, events A and B can both report deposit UUID P. Store both delivery IDs as accepted, but credit P only once. A retry of A receives 2xx without another inbox entry or credit. Record the financial effect and business key in the same transaction so a crash cannot apply one without the other.

Retries and manual replay preserve the same event ID and raw body. The delivery X-Timestamp and X-Signature are regenerated on each attempt. Verify them even when the event ID is already present.

Operational tuning

Monitor your inbox processing failures as well as the TradeOn delivery log. A delivered event means your endpoint returned 2xx; it does not mean your background worker completed its work. The sender may deliver an event more than once, and automatic retries are finite.

Webhook delivery retries apply only after an event has been stored. Event creation and business state changes do not provide a guarantee of complete catch-up after an outage. Periodically reconcile purchase history and balance, and investigate gaps using delivery IDs and your own inbox. There is no guaranteed webhook delivery latency or replay retention window documented here.