Duplicate Webhook Orders: How to Prevent Them
Prevent duplicate crypto orders with idempotency keys, durable event storage, unique client order IDs, bounded retries and position reconciliation.
Webhooks are normally delivered at least once, not exactly once. A sender may retry after a timeout even though your receiver processed the first request. Without idempotency, one alert can create two crypto orders and double exposure before anyone notices.
Short answer: assign every trading intent a stable event ID, record it durably before execution, enforce a unique client order ID at the exchange, return acknowledgments quickly, bound retries, and reconcile the resulting position. Never deduplicate only in memory.
Key takeaways
- Duplicate delivery is normal distributed-systems behavior.
- Authentication proves the sender; idempotency prevents repeated effects.
- Event IDs must represent the same trading intent across retries.
- Durable storage and a uniqueness constraint are stronger than a cache flag.
- An unknown exchange response requires lookup before retry.
- DRKN should provide risk-review guardrails without claiming execution guarantees.
Why do duplicate webhook orders happen?
The receiver can submit an order successfully but fail before returning 200 OK. The sender sees a timeout and retries. Network intermediaries, queue redelivery, manual alert replay, platform retry policies, multiple automation routes, and application restarts can all deliver the same logical event more than once.
A second failure mode occurs inside the receiver. Two workers read the same event simultaneously, both check a non-atomic “not processed” flag, and both place an order. Scaling the service increases this race. Exactly-once delivery cannot be assumed across separate systems; design for at-least-once input and an idempotent effect.
What makes an idempotency key reliable?
Use a stable identifier created by the source or deterministically derived from immutable fields such as strategy, alert instance, instrument, intended action, and event timestamp. A random ID generated separately on every retry defeats deduplication. The key must distinguish a legitimate second scale-in from a retry of the first one.
Store the event ID in a database table with a unique constraint. The first transaction claims it; later deliveries read the existing state. Record statuses such as received, validated, submitted, acknowledged, failed, and reconciled. Keep a payload hash so a reused ID with different content is rejected rather than silently accepted.
How should the order-submission flow work?
Validate the webhook signature, timestamp, schema, instrument allowlist, and risk limits before any order call. In one durable transaction, insert the event or detect that it already exists. Then enqueue the claimed event for execution and acknowledge receipt quickly if the sender’s protocol permits asynchronous processing.
At submission, send the same unique client order ID for every retry of that intent. Many exchanges let clients provide such an identifier and reject duplicates within defined rules. Save the exchange order ID and response. If the request times out, query by client order ID before deciding whether another submission is safe.
Mid-article CTA: Add DRKN-style guardrails around automation: review account exposure and permission boundaries before enabling an alert path, while keeping execution controls explicit and user-owned.
Why are retries especially dangerous in trading?
Retries are useful for transient failures but ambiguous for order creation. A 500, dropped connection, or timeout does not prove the exchange rejected the order. The order may be live while the client lacks the acknowledgment. An immediate blind retry can create a second order.
Classify failures. Definitive validation rejections should not retry. Rate limits can use bounded exponential backoff and jitter, respecting venue headers. Unknown outcomes require an order-status lookup and reconciliation. Set a maximum retry count and route unresolved events to a dead-letter queue or manual review rather than looping indefinitely.
What risk checks belong before deduplicated execution?
Idempotency prevents the same intent from executing twice; it does not make the intent safe. Apply maximum order size, allowed instruments, maximum leverage, total account exposure, price deviation, stale-alert age, trading-hours rules, and a global kill switch. Ensure a close command cannot accidentally reverse a position when reduce-only behavior is intended.
Credential design matters too. The webhook receiver should use its own narrowly scoped execution key, never a key shared with a dashboard or risk tool. Withdrawal permission is unnecessary. Restrict sources where possible and rotate credentials through a documented lifecycle.
How do you test duplicate protection?
Use a non-production environment and replay the same signed event ten times concurrently. Exactly one event claim and, where appropriate, one order intent should result. Then simulate a timeout after exchange acceptance but before database acknowledgment. The recovery path should query by client order ID and avoid resubmission.
Also test:
1. Same event ID with changed payload. 2. Two valid scale-in events with distinct IDs. 3. Worker crash after event claim. 4. Database delay and queue redelivery. 5. Rate-limit and exchange-maintenance responses. 6. Partial fills followed by a cancellation or close alert. 7. Manual replay after the deduplication retention window.
The final assertion is account state, not merely HTTP responses.
What does a practical incident response look like?
If duplicate exposure appears, pause the automation path, inspect open orders and fills, and compare exchange order IDs with event and client IDs. Do not automatically submit an opposite order until current position mode, partial fills, and pending orders are known. Revoke or disable the dedicated execution credential if new unauthorized requests continue.
After containment, determine whether the duplicate came from source replay, concurrent workers, ambiguous timeout, or manual intervention. Extend event retention beyond the sender’s maximum retry window and add an alert for any uniqueness conflict or duplicate client-order rejection.
Where does DRKN fit?
DRKN can help keep automation conversations tied to account-level risk: current exposure, leverage, liquidation distance, and permission scope. It should not claim that a review layer alone prevents duplicate execution. Durable idempotency belongs in the automation service; final trade control stays with the user.
Continue with partial-fill automation risk, isolate credentials using separate keys for bots and risk tools, and maintain the API key lifecycle.
FAQs
Is a webhook signature enough to stop duplicates?
No. It authenticates and protects the message, but the same valid message can be retried. You still need idempotency.
How long should event IDs be retained?
Longer than the source’s retry and replay window, with additional retention based on audit and operational needs.
Can a timestamp alone be the idempotency key?
Usually not. Collisions and formatting changes are possible, and retries may carry different receipt times. Use a stable event identifier.
Does DRKN place webhook orders?
No such capability is promised here. DRKN is tied to educational risk review and automation guardrails, not guaranteed execution.
Sources
- TradingView webhook alerts
- Stripe idempotent requests
- AWS Builders’ Library: Making retries safe with idempotent APIs
Risk disclaimer: Automated and leveraged trading can cause rapid losses. This is technical education, not financial advice or a guarantee against duplicate orders. Human review and venue-specific testing are required before production use.