On this page
- Quick read
- Readiness check
- Define the replay window as a testable rule
- Write the ticket before the bench opens
- Build signed fixtures that include stale timestamps
- Keep the signing secret out of the test lane
- Record what the receiver did with each delivery
- Cover clock skew, parallel duplicates, and rotated secrets
- Set review cadence and measures with denominators
- Close with a replay packet a second reviewer can run
- Role brief
- Frequently asked questions
- Sources
Is the handoff ready?
Use these checks before access expands or the first ticket begins.
Rule
Ready: Tolerance, identifier store, and expected status written per case
Needs work: A vague instruction to validate signatures
Fixtures
Ready: Generated payloads with signed timestamps and duplicate identifiers
Needs work: One copied production request
Access
Ready: Repository read, sandbox deploy, test secret only
Needs work: Sender dashboard and production secret
Evidence
Ready: Command, redacted output, status, stored identifier, side effect
Needs work: A screenshot of a green check
Define the replay window as a testable rule
A webhook receiver only deserves trust when it rejects an old signature, accepts a fresh one, and records enough detail to explain the difference. Write that rule down before a Philippines-based programmer touches the integration, including the timestamp tolerance, the identifier that prevents duplicates, and the response the sender should see in each case.
Most teams describe the replay window in one sentence and then test it by firing a happy request. That proves almost nothing. The bench needs a stale signed payload, a fresh signed payload, a replayed identifier inside the window, and the same identifier after the window closes.
- Signed at now minus tolerance minus one minute: reject with a named status code.
- Signed at now: accept once and store the delivery identifier.
- Same identifier repeated inside the window: ignore or reject by rule, never reprocess.
- Identifier reused after the window: reject and raise an alert rather than dropping it silently.
Write the ticket before the bench opens
The ticket should name the endpoint, the event type, the secret version, the tolerance value, the table or cache that holds seen identifiers, and the person who owns the signing secret. Ask for a branch, a fixture list, one test command, and the observed response code for every case.
Keep the first ticket to one event type and one receiver. A programmer working from Manila can usually finish that in a day or two, and your reviewer can replay the same commands without a call. A broad request to harden the whole webhook layer produces a long branch and vague evidence.
Build signed fixtures that include stale timestamps
Generate fixtures from code, not from a copied production request. A small script that takes a body, a secret, and a timestamp keeps the test honest and lets the reviewer regenerate the header later. Store the body, the header, the tolerance, and the expected status beside each other.
Do not put the real signing secret in the fixture repository. Use a test secret that exists only in the sandbox, and let the company owner rotate the production value. If the receiver needs the secret to boot, load it from the environment and fail closed when it is missing.
Stale timestamps are the case most likely to be skipped. Put the tolerance in configuration, not in the test file, then prove the boundary at tolerance minus one second and tolerance plus one second. Boundary proof is what turns a claim into a rule.
Keep the signing secret out of the test lane
Named accounts matter more here than in most tickets. The programmer needs read access to the receiver repository, a sandbox deploy target, and a test secret. They do not need the sender dashboard, the production secret, or the ability to disable signature checks for a demonstration.
Write the stop rule in the ticket. If a test requires a production payload, real customer identifiers, or a relaxed check in a shared environment, work stops and the company owner decides. A replay test that weakens verification in order to pass is worse than no test at all.
Record what the receiver did with each delivery
Evidence should show the delivery identifier, the timestamp, the signature header, the tolerance in force, the body hash, the stored identifier, the response status, and whether a side effect ran. A screenshot of a green check is not evidence for a replay control.
Ask the programmer to attach the command and the redacted output. If the receiver logs to a table, include the query and the rows it returned. The reviewer should be able to see that the duplicate was suppressed by a stored identifier rather than by timing luck.
Name every item that could not be verified. A missing sandbox sender, an absent clock source, or a queue that cannot be inspected belongs in the pull request summary, not in a private message that disappears when the contract ends.
Cover clock skew, parallel duplicates, and rotated secrets
Adverse cases belong in the same ticket. Move the test host clock forward and backward, send two deliveries in parallel, rotate the secret while a payload is in flight, and send an unsigned request. Each case needs a defined outcome and an owner who can accept the result.
The parallel case exposes the real weakness. A check that reads, compares, and writes in three steps will pass a sequential test and fail under concurrency. Ask for the unique constraint, lock, or idempotency key that makes the second write harmless.
- Clock skew: reject outside tolerance and log the observed offset.
- Unsigned request: reject before parsing the body.
- Rotated secret: accept the new version and reject the retired one after the declared cutover.
- Duplicate in flight: one side effect, one stored identifier, two recorded responses.
Set review cadence and measures with denominators
Count eligible deliveries, not just failures. The useful measure is rejected stale signatures divided by all stale signatures presented, alongside accepted fresh signatures divided by all fresh signatures presented. Add duplicate side effects per one thousand accepted deliveries.
Review the numbers weekly while the integration is new and monthly after two clean cycles. Attribute wait time to the owner who caused it: sender dashboard access, reviewer availability, sandbox availability, or secret rotation approval. Do not fold those waits into a single delivery score.
Pair each rate with the count of untested cases. A rejection rate of one hundred percent across two fixtures is not a mature control, and the reviewer should say so before the number reaches a status report.
Close with a replay packet a second reviewer can run
The handoff contains the tolerance rule, the fixture generator revision, the secret owner, the seen-identifier store, the test commands, the observed status for each case, and the remaining risks. Add the date of the last secret rotation and the next review date.
Close the ticket only when another reviewer can reproduce the stale rejection and the duplicate suppression without asking the programmer for help. That reproduction, not the merged branch, is the durable output of the work.
A short index of the fixtures and their expected statuses saves the next reviewer from reading the whole pull request. Keep it beside the generator so the two cannot drift apart silently.
Copy-ready brief
Put the operating agreement in writing.
One receiver, one event type, sandbox deploy only.
Generated bodies with fresh, stale, duplicated, and unsigned headers.
Command, redacted output, response status, identifier row, side effect count.
Company owner holds the production secret, tolerance change, and merge.
Frequently asked questions
Who decides the timestamp tolerance?
The company owner sets the tolerance with the sender team. The programmer tests the declared value and reports the observed boundary, not a preferred number.
Can the test use a captured production payload?
Only after the owner approves redaction and storage. A synthetic body is usually enough and avoids moving customer data into a test repository.
What makes the duplicate test pass?
One side effect, one stored delivery identifier, and two recorded responses that follow the written rule.
Sources
- OWASP Application Security Verification Standard
Verification categories for authentication, session, and input handling that frame the signature check.
- NIST Secure Software Development Framework
Practices for protecting code, secrets, and deployment environments.
- GitHub pull request reviews
First-party description of review, approval, and requested changes.
- OpenTelemetry documentation
Guidance for recording delivery identifiers and outcomes in traces and logs.
