Skip to main content
A webhook is just an HTTP POST from someone claiming to be your provider. Verifying its signature is what turns that claim into trust. Register a provider’s signing secret on an endpoint and webhook.co verifies every inbound request for you — against the exact bytes it captured — and records the result on the event. You don’t write any signature code.

Register a signing secret

Add the secret your provider issued to the endpoint that receives its webhooks. It’s sealed in a KMS the moment it arrives and never stored in the clear.
wbhk endpoints add-provider-secret <endpoint-id> \
  --provider stripe --label "prod signing secret"
# paste the secret when prompted (or pipe it on stdin)
curl -X POST https://api.webhook.co/v1/endpoints/<endpoint-id>/provider-secrets \
  -H "Authorization: Bearer $WEBHOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider":"stripe","secret":"whsec_...","label":"prod signing secret"}'
await webhook.endpoints.addProviderSecret({
  endpointId,
  provider: "stripe",
  secret: process.env.STRIPE_SIGNING_SECRET!,
  label: "prod signing secret",
});
That’s it. From the next request on, webhook.co checks the signature and stamps each event with a verification state. Registering a secret requires the endpoints:write scope; adding, listing, and revoking secrets is available on the CLI, API, dashboard, and MCP alike.
webhook.co picks the right scheme by which secrets you’ve registered, not by trusting a header the sender set — so providers that share a signature header each verify correctly. It checks the registered providers against the captured bytes and the first that verifies names the event’s provider.

The four verification states

Every event carries a verificationState. It’s the difference between “this is really from Stripe” and “someone POSTed to your URL.”
StateMeaning
verifiedA cryptographic signature over the payload was checked and passed.
authenticatedThe source proved itself with a shared static token or HTTP Basic — weaker than a payload signature, but not anonymous.
failedA signature was checked and rejected — forged or tampered. These are never delivered onward or signed with your secret.
unattemptedNo signature was checked (no secret registered, or the header was absent). The event is still captured, but never re-signed as if it were vouched-for.
Filter your events by state to confirm a provider is set up correctly — a stream that’s all unattempted means the secret isn’t registered yet; a failed means the signature didn’t match.
wbhk events list <endpoint-id> --status verified,failed

If you verify a signature yourself

webhook.co verifies inbound signatures for you, but the same rule governs any signature you check in your own code — including the signature webhook.co adds when it delivers to your service:
Always verify over the raw request body, byte-for-byte, before any JSON parsing or re-serialization — re-encoding changes the bytes and breaks the signature. And compare digests with a constant-time function (crypto.timingSafeEqual, hmac.compare_digest), never ==. This is the single most common mistake in webhook verification.

Providers

webhook.co verifies 142 providers out of the box, from a single registry. See the provider directory for the exact scheme, header, and handshake behaviour of each, the deep-dive guides for the trickier ones, or bring your own if a provider signs with a standard HMAC we don’t list by name.