OffenderSearch
Free developer tool · runs in your browser

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.

1 · Send a test webhook

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.

Header the receiver will see
X-Offendersearch-Signature: computing…
Body (exact bytes; shown pretty, sent compact)
{
  "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.

2 · Test your webhook URL

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.

3 · Verify a delivery you received

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.

Not checked yet

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.

What this is

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

Test a webhook URL

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.

Signed vs unsigned

Why a webhook test should check the signature, not just the 200 A 200 proves a route exists. A signature proves the rest.

CheckUnsigned webhookSigned webhook
Did a POST arrive?YesYes
Did it come from the sender?Unknown — anyone can POST JSON to a URLYes, if the HMAC matches a secret only the sender holds
Was the body altered in transit?UnknownNo, if the HMAC matches
Is it a replay of an old delivery?UnknownRefused if the timestamp is outside the window
What a test must proveThe route existsThe 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.

The three cases

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 200 within 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 answer 401 and 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 200 both times and process the event once, keyed on id (or alertId).
Privacy

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?
Sign a sample body with your secret, POST it to your endpoint with the signature header, and confirm the receiver verifies it, refuses a tampered or stale copy, and processes a duplicate once. This page produces all of those cases.
How do I test a webhook URL?
Run the curl from step 2 against it. If the URL is not public yet, expose your local server with ngrok, or use a webhook.site address to see what arrives.
Can I send a test webhook from here?
The page signs the event and gives you the curl; you send it from your own machine. We never POST from our servers to a URL typed on a web page.
What is a webhook test endpoint?
Any URL that receives the POST so you can inspect it: your own route, an ngrok tunnel to it, or a hosted inbox such as webhook.site. For verification you still need your own code, because the secret must not leave your side.
How do I curl a webhook test?
Compute HMAC-SHA256 over "<unix seconds>." plus the exact body with your secret, put it in the X-Offendersearch-Signature header as t=<seconds>,v1=<hex>, and POST the body with --data-binary. Step 2 writes the command for you.
Is there a webhook dashboard?
For deliveries the platform made, GET /v1/fcra/events lists every event with its delivery status, attempts, last status code and next attempt time, and POST /v1/fcra/events/{id}/resend sends one again. Monitoring keeps the alert history per monitor.
Why does my signature not verify?
Almost always the body bytes differ from what was signed: a framework parsed and re-serialised the JSON, a newline was added, or the wrong environment’s secret was used. Verify on the raw request body, with the timestamp from the header.
Does the test cost anything?
No. The tool is free and needs no account. Running the FCRA or monitoring flows end to end in the sandbox with a test key is also free.

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.