Verification is driven by what you registered, not by headers
A naive verifier trusts the request to say who signed it. webhook.co doesn’t. Detection from headers is only a hint — a fast path for the common single-provider endpoint. The actual work is registration-driven:- You register a provider secret on an endpoint (say,
stripe). - A request arrives. webhook.co captures the raw bytes first — verification never blocks or delays ingest.
- It runs the adapters for the providers you registered, trying the header-detected hint first, then the rest. Each adapter recomputes the signature over the exact captured bytes and constant-time-compares.
- The first adapter to match wins; the event is marked
verified(orauthenticated). If none matches, the event is still captured — it’s just unverified.
The registry: 142 providers
Every provider webhook.co recognizes lives in a single registry of 142 providers. Two kinds of entry sit behind that number:- 109 are one declarative config row — a header name, a digest (SHA-256, SHA-1, or SHA-512), an encoding, and how the signed message is assembled. Every one runs through the same audited HMAC core, so the per-provider surface is data, not code.
- 33 are hand-written adapters — the schemes that don’t reduce to a single HMAC recipe: JWT (HS256), asymmetric signatures (Ed25519, ECDSA, RSA), remote-key-fetch (the verification key is fetched from the provider and host-pinned), request-context HMAC (signing the URL, form fields, or specific body fields), and non-cryptographic token/basic authenticity.
Handshakes are answered automatically
Some providers won’t start delivering until your ingest URL answers a one-time verification challenge — a GET or POST carrying a nonce, a subscription token, or a signed ping. webhook.co answers these for you: Slack, Meta, Dropbox, X, eBay, Braintree, Okta, Microsoft Graph, Twitch, monday.com, Asana, Zoom, and Discord all complete setup against a wbhk.my URL with no action on your part.Handshakes are control messages, not events. They’re dispatched before capture and capture and
meter nothing — a challenge never shows up in your event stream or counts toward usage. Where a
handshake computes a response under your secret, it’s fenced so it can’t be coaxed into signing an
attacker-chosen payload.
The four verification states
Every stored event carries exactly one state, derived from what the server actually observed — never from a client-supplied claim:| State | Meaning |
|---|---|
verified | An adapter reproduced the cryptographic signature over the captured bytes. |
authenticated | The source proved itself with a shared static token or HTTP Basic credential — real, but weaker than a payload signature, so it’s a distinct state (never dressed up as verified). |
failed | An adapter ran and rejected — a signature was present and did not match. Worth investigating. |
unattempted | No signature was checked: no secret registered, the expected header was absent, or a rare internal error. Not a failure — just unverified. |
unattempted and failed are deliberately different. An event with no registered secret is unattempted, not failed — absence of a check is not evidence of forgery.
Reading the directory
The provider directory is generated from the same registry the engine runs. For each provider it lists the slug you register the secret under, the signature scheme, the exact signature header, and whether the provider uses a handshake. Match your sender to its row, register the secret, and you’re verifying.Verify inbound signatures
The step-by-step guide to turning on verification for an endpoint.
Provider directory
All 142 providers, their slugs, schemes, and headers.
Custom or unlisted provider
What to do when your sender isn’t individually listed.
Inbound verification concepts
Where verification sits in the receive pipeline.