Webhook receiver

A webhook receiver in Node and Python (Flask) that verifies Standard Webhooks signatures on the raw body, replies fast, ignores duplicates and fetches job results.

Last updated

View as Markdown

This recipe receives job.completed and lookup.completed events and verifies them before trusting them. There is a Node version with no framework and a Python version with Flask.

What rules does the receiver follow?

RuleWhy
Verify the signature on the raw bodyRe-serialized JSON changes the bytes, so the signature no longer matches
Reply with 2xx quickly, then do the workSlow or failing endpoints are retried
Ignore repeated webhook-id valuesA delivery can arrive more than once
Fetch job results with your API keyEvents never contain phone numbers or e-mail addresses

What does verification look like?

JavaScript
import { verifyWebhook } from "mobilevalidate";

const raw = Buffer.concat(chunks);                 // the exact bytes received
const event = await verifyWebhook(raw, req.headers, process.env.MOBILEVALIDATE_WEBHOOK_SECRET);
res.writeHead(204).end();                          // reply first
if (event.type === "job.completed") {
  for await (const row of mv.jobs.results(event.data.id)) { /* … */ }
}
Python
from mobilevalidate import verify_webhook, WebhookVerificationError

@app.post("/webhooks/mobilevalidate")
def receive():
    try:
        event = verify_webhook(request.get_data(), request.headers, SECRET)
    except WebhookVerificationError as e:
        return str(e), 400
    return "", 204

verifyWebhook throws when the signature doesn't match or the timestamp is more than 5 minutes off. See Webhooks for the signature scheme and retry schedule.

How do I test it locally?

Sign a sample event with the CLI and send it with curl. No API key is needed for this:

Shell
export MOBILEVALIDATE_WEBHOOK_SECRET=whsec_$(printf 'local-test-secret' | base64)
npx mobilevalidate webhooks sign --secret "$MOBILEVALIDATE_WEBHOOK_SECRET" --file event.json > headers.txt
curl -i localhost:3000/webhooks/mobilevalidate -H 'content-type: application/json' \
  -H "$(sed -n 1p headers.txt)" -H "$(sed -n 2p headers.txt)" -H "$(sed -n 3p headers.txt)" \
  --data-binary @event.json

To receive real events, register an endpoint with a personal test key or a live key.

Frequently asked questions

Why does my signature check fail?

Usually because the body was parsed and re-serialized before verification. Verify the raw bytes exactly as received, with the endpoint's own whsec_ secret.

Can I use webhooks with the sandbox key?

No. The public sandbox key has no webhooks. Use a personal test key to register a test endpoint, or sign a sample event yourself with the CLI.