Test a webhook: signed sample events, verified in the browser
Pick a real event, sign it with a secret, copy a curl to your webhook URL, and paste back what you received to check the signature. The same header format every OffenderSearch webhook uses, so a receiver that passes here passes in production.
Pick an event, get a signed sample
Every type the platform sends, with the data keys it really carries. The body is serialised exactly as the sender does it.
X-Offendersearch-Signature: computing…{
"createdAt": "2026-10-28T00:00:00+00:00",
"data": {
"orderId": "ord_4f2a9c7e1b3d5f8a0c2e",
"status": "complete"
},
"id": "fev_7c1d9e2a4b6f8a0c1e3d",
"type": "order.completed"
}Signed with the current time. The header changes every second you re-sign.
Copy a signed POST to your own endpoint
We never POST from our servers to a URL you type here. Run this from your machine against your receiver, an ngrok tunnel, or a webhook.site address to see exactly what arrives.
curl -X POST 'https://webhook.site/your-unique-id' \
-H "Content-Type: application/json" \
-H "X-Offendersearch-Signature: t=…,v1=…" \
-H "X-Offendersearch-Event: order.completed" \
-H "X-Offendersearch-Event-Id: fev_7c1d9e2a4b6f8a0c1e3d" \
--data-binary '{"createdAt":"2026-10-28T00:00:00+00:00","data":{"orderId":"ord_4f2a9c7e1b3d5f8a0c2e","status":"complete"},"id":"fev_7c1d9e2a4b6f8a0c1e3d","type":"order.completed"}'Store the secret above as OS_WEBHOOK_SECRET in your receiver first; the sample verifies only against the secret that signed it.
Paste the header, the raw body and your secret
The check runs in this tab with WebCrypto; nothing is uploaded. It does what your receiver should do: parse t and v1, refuse a stale timestamp, recompute HMAC-SHA256 over "<t>." + body, compare in constant time.
Self-check running On load, this page signs a fixed body with a fixed secret and compares the result to the signature the server’s own Python signer produced for it (t=1761609600, v1=1=2ea9b523e540f3…). A match means the browser implementation and the sender agree byte for byte.
How to test a webhook before any real event fires Four steps, no account.
A webhook test answers three questions: does my URL receive a POST, does my code verify the signature, and does it handle a replay. You do not need a real event for any of them.
- 01
Pick an event type
Choose one of the real event types the platform sends; the tool builds the exact JSON body the sender would POST.
- 02
Sign it with a secret
Generate or paste a whsec_ signing secret. The X-Offendersearch-Signature header is computed in your browser with HMAC-SHA256 over "<t>." + body.
- 03
POST it to your webhook URL
Copy the curl command and run it from your machine against your endpoint, an ngrok tunnel, or a webhook.site address.
- 04
Verify what you received
Paste the header, the raw body and your secret; the tool reports valid, invalid or stale, with the reason.
The format is the one every OffenderSearch webhook uses: FCRA events (reference) and Monitoring alerts (delivery) sign the same way, so one verifier serves both. The scheme is explained line by line on API signature.
Testing a webhook URL that is not public yet ngrok, webhook.site, then the sandbox.
A webhook URL must be reachable from the internet over HTTPS, and the sender will not follow a redirect or call a private address. While your receiver still runs on a laptop, expose it with an ngrok tunnel and use the tunnel address in the curl above; the request reaches your local process with the real headers.
To see a delivery without any code at all, point the curl at a webhook.site address: it shows the method, headers and raw body as they arrived. Compare the body bytes it displays with the body shown in step 1; they must be identical for a signature to verify.
When the receiver is live, register its URL with PUT /v1/fcra/webhook-endpoint (or as channels.webhookUrl on a monitor), store the secret the response returns once, and run the sandbox flows with a test key for an end-to-end rehearsal that costs nothing.
Why a webhook test should check the signature, not just the 200 A 200 proves a route exists. A signature proves the rest.
| Check | Unsigned webhook | Signed webhook |
|---|---|---|
| Did a POST arrive? | Yes | Yes |
| Did it come from the sender? | Unknown — anyone can POST JSON to a URL | Yes, if the HMAC matches a secret only the sender holds |
| Was the body altered in transit? | Unknown | No, if the HMAC matches |
| Is it a replay of an old delivery? | Unknown | Refused if the timestamp is outside the window |
| What a test must prove | The route exists | The route exists, verifies on raw bytes, refuses stale and tampered deliveries, and dedupes on the event id |
The webhook example has a complete receiver in Express and FastAPI that passes all four checks.
Send a test webhook for each case your receiver must handle Valid, tampered, stale, duplicate.
- Valid. Sign as of now, POST it. Your receiver must answer
200within 10 seconds and process the event once. - Tampered. Change one character of the body after signing (or one hex digit of
v1) and POST it. Your receiver must answer401and process nothing. - Stale. Sign as of one hour ago and POST it. Your receiver must refuse it as outside the replay window, even though the HMAC itself is correct.
- Duplicate. POST the valid delivery twice. Your receiver must answer
200both times and process the event once, keyed onid(oralertId).
A webhook tester that never sees your secret Everything runs in the tab.
Everything on this page runs in your browser with WebCrypto. The secret, header and body you type are not uploaded, logged or stored, and the page does not POST to the URL you enter: it gives you a curl to run yourself. The implementation is the open-source lib/webhook-signature.ts in the site’s repository, and it is tested against the server’s Python signer with the known-answer vector the self-check uses.
Webhook testing FAQ
How do I test a webhook?
How do I test a webhook URL?
Can I send a test webhook from here?
What is a webhook test endpoint?
How do I curl a webhook test?
Is there a webhook dashboard?
Why does my signature not verify?
Does the test cost anything?
Keep going The guides this tool is built for.
Wire a real webhook next
25 free searches, no card. Register a webhook URL, get the signing secret once, and rehearse every event in the sandbox.