Skip to main content
Time: about 25 minutes · Level: intermediate · You’ll build: a webhook receiver that rejects forged requests, handles retries safely, and records what it processed.

Scenario

When an invoice is paid, your system should activate the customer’s paid features. Webhooks deliver that event, but endpoints get retried, arrive out of order, and can be spoofed. This tutorial builds a receiver that handles all three.

Prerequisites

  • A public HTTPS endpoint, or a tunnel such as ngrok for local testing.
  • A signed-in user session to register the webhook. Webhook management requires a user session, not an API key.
  • Node.js 20 or later.
1

Register the endpoint

Subscribe to the two events this tutorial handles. Replace the URL with your endpoint.
Save the secret that starts with whsec_. It’s needed to verify every delivery, and it’s shown in this response.
Endpoints must use https in production. Store the secret in your secret manager, never in source control.
2

Verify the signature on the raw body

Parsing the JSON before you verify changes the bytes, so verify first. Using Express:
Return 401 for a bad signature and 400 for missing headers. Don’t return 200 for an unverified request.
3

Process each event exactly once

Orvlin retries a delivery until it gets a 2xx, so the same event can arrive more than once. Record each event ID the first time you process it, and skip repeats.
Mark the event as processed in the same database transaction as the side effect. If the side effect fails, the mark rolls back, and the retry runs it again.
4

Test a delivery locally

Send yourself a signed request with the same method Orvlin uses. The sample below signs a body with the secret from step 1:
You should get 200. Send the exact same request again: it should also return 200, and the handler should log nothing new. Change one character of the body and send it: you should get 401.
5

Check deliveries and recover from failures

Orvlin records each delivery attempt. If your endpoint was down, look at the failures, fix the endpoint, and let Orvlin retry:
Orvlin retries failed deliveries with increasing delays for about a day. Use the test endpoint to check the endpoint after a fix:

Production checklist

  • Verify every request before you parse it.
  • Compare signatures with a constant-time function.
  • Reject timestamps older than a few minutes.
  • Make handlers idempotent, keyed on the event id.
  • Respond quickly, and do slow work from a queue.
  • Rotate the secret if it leaks, then update the endpoint.

Next steps

Webhooks reference

Every event, header, and management endpoint.

Reliability

How Orvlin retries, what it guarantees, and what it doesn’t.