Reliability / Webhooks
Order webhook retries: verify, deduplicate, apply once
Your agent created one order. Its webhook can still arrive twice. A receiver needs its own retry boundary before it changes stock, sends a receipt or starts fulfillment.
Verify the raw body, claim the delivery in a durable transaction, and commit the local update before acknowledging it. A valid signature alone does not make a delivery new.
The timeout after the update
A checkout completes and OrderCore sends checkout.completed. Your receiver records the event, but the response is lost. The sender cannot tell whether your update succeeded, so a retry can arrive. Running the same business action again can produce duplicate emails or fulfillment jobs even when the original checkout created only one order.
This is a different boundary from idempotent order creation. The API protects the order write; your webhook consumer must protect its own effects.
1. Verify before you act
Read the unmodified request body and verify X-OrderCore-Signature. The expected value is sha256= followed by the hex HMAC-SHA256 of those exact bytes, using the secret for the receiving endpoint. Compare signatures in constant time. Parsing and reserializing JSON first can change the bytes and invalidate a legitimate signature.
Resolve the endpoint and merchant from trusted server configuration. Do not select a merchant solely from an unverified header. The webhook reference describes the headers and signature format.
2. Keep a durable delivery inbox
OrderCore repeats the same X-OrderCore-Delivery-ID when it retries a delivery. Store that ID under the receiving merchant or endpoint namespace and enforce uniqueness in the database. An in-memory set disappears when your process restarts, and a separate “have I seen this?” query can race with another worker.
The inbox insert and the local business update must be in the same transaction. If the update fails, roll back the inbox claim too. Otherwise a retry can be skipped even though the original action never committed.
- VerifyAuthenticate the exact body bytes.
- ClaimInsert the delivery with a unique key.
- ApplyCommit the local update and inbox together.
3. Protect the business action too
Each registered endpoint has its own delivery ID for the same event. If several endpoints feed one consumer, delivery-level deduplication alone is insufficient. For a one-time checkout-completed action, also use a unique business key such as (merchant, order_id, action).
Read the event type and order ID from the verified body. The signature authenticates the body; the delivery ID header is not included in that body signature. A stable business key also prevents a captured valid payload with a different delivery ID from repeating that one-time action. Other event types may need an event version or another explicit identity; do not blindly suppress every future update for an order.
Run the offline test
This Python example signs a synthetic checkout event and applies it to a temporary SQLite database. It closes and reopens the database before delivering the same event again. It also checks a tampered body and the same event arriving under another delivery ID.
Download the standalone Python demo. It uses only the standard library and makes no network or payment calls. Save it, inspect it, then run:
python3 webhook-retries-demo.py First delivery: order updated Retry after restart: duplicate skipped Order updates: 1 Tampered body: invalid signature Other delivery ID: order already updated PASS: all checks; one local order update.
The example demonstrates a durable local transaction. It is not an HTTP receiver, a shipment integration or a claim of exactly-once network delivery. No real customer order is created.
Before using this in a live receiver
- Return success after the durable commit, including for an already committed duplicate. A failed transaction should remain retryable.
- Put external work in a transactional outbox. A database transaction cannot atomically include an email service or a shipping API call.
- Give the downstream action a stable idempotency key where its provider supports one. Recovery must handle a crash after the external call but before its local acknowledgment.
- Choose inbox retention for your retry and replay policy. Expiring an entry can make an old delivery look new; retain the business action identity when the action must happen only once.
- Log delivery ID, order ID and outcome, without logging endpoint secrets. Monitor failures and abandoned deliveries using the webhook delivery endpoints.
Follow an order from checkout to your receiver.
Start with the offline checkout demo, then use the webhook reference to connect your own consumer.