Trading webhook replay protection combines an authenticated request, a signed delivery timestamp and a durable record of accepted event IDs. A valid signature alone cannot stop someone from sending the same signed message again. A trading workflow must also check the original signal's age, because a newly delivered request can carry an instruction that is already too old to trade.
This article was prepared with AI assistance using the primary documentation linked below. The receiver rules and test cases are an illustrative design exercise, not a claim about a particular deployed integration or a recommendation to place a trade.
Why an authentic alert can still be unsafe
Imagine an alert that means “evaluate a BTC entry after this candle closes.” The receiver accepts it, but its response is lost. The sender tries again. Alternatively, somebody who obtained the original request resends it. Both deliveries can contain the same authentic instruction.
The receiver needs to distinguish four questions: who authorized this message, whether this delivery is recent, whether the event was accepted before, and whether the underlying signal remains usable. Each needs evidence. HTTPS protects transport; a signature authenticates covered message content; neither automatically records that an event has already been consumed.
Retries are ordinary delivery behavior. For example, TradingView's resubmission documentation says an HTTP 500–599 response, except 504, can trigger a resend after five seconds, with up to three resends. That is one provider's policy, not a general rule for timeouts or every webhook service.
The goal is to admit an event once, retain evidence of the admission, and let the trading rules decide whether any order is justified. Admission does not equal execution.
Protect the fields that decide admission
Use the sender's documented verification library and signature format. Inventing a similar-looking format can leave important fields outside authentication. The Standard Webhooks specification describes signing the event ID, attempt timestamp and body together. It distinguishes a stable event identity from an attempt timestamp that can change on retry.
That distinction matters. If an attacker can change an unsigned event ID, an ID cache may see a supposedly new event. If they can change an unsigned timestamp, an old request may look fresh. The decision fields must be authenticated under the protocol the receiver actually supports.
Preserve the raw body until verification is complete. Parsing JSON and serializing it again can change whitespace or field order, producing different signed bytes. Use the supported constant-time comparison for HMAC verification rather than ordinary string equality. Treat secrets as endpoint credentials and keep them out of logs.
Different providers offer different authentication mechanisms. Do not assume that a chart alert supplies arbitrary signed headers. TradingView's webhook setup guide describes a POST containing the alert message; its separate webhook authentication guide describes HTTPS client-certificate verification configured by the recipient. Neither establishes compatibility with another platform's HMAC format. Verify the whole route, including any relay.
A relay's signature proves what the relay signed. Its newly generated event ID or timestamp does not, by itself, prove when an upstream alert originated or whether the relay received it before.
Keep two clocks
The delivery clock measures when this request attempt was signed. The signal clock measures when the trading condition occurred. Record both, plus the receiver's arrival time, with explicit units and timezone.
Stripe's replay guidance illustrates the delivery clock: its signatures cover a timestamp, its libraries default to a five-minute tolerance, and a retry receives a new signature and timestamp. This is a payment-webhook example of a security mechanism, not a trading freshness recommendation.
For a trading signal, define a separate expiry based on the strategy's decision horizon. A five-minute transport tolerance could coexist with a thirty-second signal lifetime. Widening the former to accommodate outages should not silently widen the latter. Check market-data freshness again before a delayed worker makes a decision.
For bar-driven alerts, retain the specific completed candle and its saved inputs. The candle-close workflow contract explains why a delayed worker must not substitute a newer live candle.
Reject timestamps outside the allowed future skew as well as those too far in the past. A clock that is minutes ahead can otherwise create a long effective acceptance period. Monitor clock synchronization; an operator should see a clock incident instead of quietly disabling freshness checks.
A worked admission worksheet
Assume a hypothetical receiver reads 12:00:30 UTC. Its documented exercise policy accepts delivery attempts no more than 120 seconds old or 5 seconds ahead, and signals no more than 60 seconds old with no future signal time. These numbers are examples, not safe defaults. Event IDs are authenticated and scoped to one sender, strategy and environment.
| Case | Signed attempt time | Original signal time | ID record | Result under the exercise policy |
|---|---|---|---|---|
| First delivery A | 12:00:29 | 12:00:00 | Unseen | Delivery age 1 second; signal age 30 seconds. Eligible for admission. |
| Duplicate A | 12:00:29 | 12:00:00 | Already accepted | Fresh but already consumed. Do not create another admission. |
| Newly signed stale signal B | 12:00:29 | 11:58:00 | Unseen | Delivery age 1 second; signal age 150 seconds. Reject as stale. |
| Captured old request C | 11:57:00 | 11:56:59 | Unseen | Delivery age 210 seconds. Reject the delivery. |
| Future-dated request D | 12:00:50 | 12:00:00 | Unseen | Attempt is 20 seconds ahead. Reject for clock skew. |
An eligible request must still pass schema validation, account routing, current market checks and risk limits. The table deliberately stops at the admission boundary. For uncertain exchange submissions after that boundary, follow order timeout reconciliation; accepting the webhook once does not resolve an exchange response that went missing.
Repeat this worksheet with your actual delivery and signal lifetimes. Include a request exactly at each boundary and one just beyond it. Write down whether boundaries are inclusive so two components do not disagree.
Make the event record survive concurrency and restarts
A local list in one running server cannot protect another worker or survive a restart. Two requests can arrive together, both observe “unseen,” and both proceed. The admission store needs a shared, atomic claim for the authenticated identity. A unique database constraint or an appropriate atomic store operation can supply that boundary.
Scope the record carefully. The same symbol and side can occur in two legitimate signals; using only “BTC buy” as a duplicate key would suppress real events. Conversely, allowing an untrusted payload to choose another account or environment creates a routing problem even when its event ID is unique. Bind allowed routing to the authenticated sender's configuration. Review exchange API permissions separately.
Retain accepted identities for the sender's supported retry and redelivery horizon, with an explicit recovery policy. A short cache that outlives the delivery window may still be insufficient when the sender can re-sign an old event with a fresh attempt time. Preserve the original signal time and durable outcome rather than treating a fresh envelope as a fresh opportunity.
Acknowledgement also needs a defined meaning. Store an accepted event and its pending work durably before reporting successful acceptance. If an outage occurs between those operations, recovery must discover the gap. A duplicate of an already stored event can receive the protocol's success acknowledgement without creating more work. If the store is unavailable, fail closed for new trading admissions and expose the incident.
Test the rejection path before live execution
Build a redacted evidence record for each case: sender scope, event ID, payload digest, signed timestamp, signal time, arrival time, admission result and reason. Do not log signing secrets, exchange credentials or reusable authentication headers.
Use an isolated receiver test harness or a supported paper environment to send an exact duplicate, a delayed message, a modified body, two concurrent copies and a replay after receiver restart. A Live activation control is not a testing substitute. Test a fresh signature over a stale original signal. Also test a previously accepted ID paired with changed immutable content: flag the conflict for investigation instead of treating it as a new instruction.
During recovery, keep historical replay separate from authorization for a new trade. Do not delete event records or reset signal timestamps merely to make a failed test pass. A human approval step can authorize a new decision after checking current conditions; it should not erase the earlier event's history. The workflow review and activation guide provides the broader workflow review context.
A replay-resistant receiver can still accept a bad strategy signal, an authorized but excessive size, or a message signed with a compromised key. Its useful guarantee is narrower: authenticated, timely events cross a controlled admission boundary with a record that explains what happened. Risk approval and exchange execution still need their own controls.
