When you open a captured webhook and see a 4 KB JSON blob with a header named x-foo-signature, the first question is always: who sent this? Stripe? GitHub? Some forgotten integration from last quarter?
Until now you'd answer that by scrolling through headers. Today you don't — the dashboard tells you at a glance.
Every captured request now shows a provider badge (icon + name) and the detected event type next to the standard metadata. A Stripe request shows the Stripe brand color, the "Stripe" label, and checkout.session.completed beside the request ID. A GitHub push event shows the GitHub mark and push. Nothing to configure — detection runs automatically on every request.
How detection works
The detector inspects request headers first, body second. Each provider has a fingerprint:
| Provider | Detected by |
|---|---|
| Stripe | stripe-signature header → event from type field |
| GitHub | x-github-event header → event from header value |
| Shopify | x-shopify-topic header → event from header value |
| Slack | x-slack-signature header → event from event.type or type |
| Twilio | x-twilio-signature header |
| Paddle | paddle-signature header → event from event_type |
| Linear | linear-signature header → event from action + type |
| Vercel | x-vercel-signature header → event from type |
| GitLab | x-gitlab-event header → event from header value |
| Clerk | svix-id headers → event from type |
| Discord | x-signature-ed25519 header → event from t field |
| Typeform | typeform-signature header → event from event_type |
| SendGrid | Body matches SendGrid event array shape |
| Standard Webhooks | webhook-id + webhook-signature + webhook-timestamp headers |
The detector is ordered — specific signatures win over generic ones. Clerk (svix-id) is checked before Standard Webhooks (webhook-signature) so Clerk requests don't fall through to the generic detector.
Detection is best-effort. If nothing matches, the request shows no provider badge — but it still appears in the dashboard with the same headers, body, and verification status it always had.
Typeform joins the provider list
This release adds Typeform as the 14th supported provider. Send a real Typeform form_response event and it's detected, signed templates work in the SDK, and signature verification works the same way it does for Stripe or GitHub:
await client.sendTo("http://localhost:3000/api/webhooks/typeform", {
provider: "typeform",
template: "form_response",
secret: process.env.TYPEFORM_WEBHOOK_SECRET!,
});The template generates the typeform-signature header (sha256=...) and a realistic form_response body. Verification handles it with the same verifySignature() call as every other provider.
Detection in your code
The dashboard's detection runs on the SDK's detectWebhookInfo() helper, also exported for use in your own code:
import { detectWebhookInfo } from "@webhooks-cc/sdk";
const info = detectWebhookInfo({
headers: request.headers,
body: request.body,
});
// info?.provider → "stripe"
// info?.event → "checkout.session.completed"
// info?.via → "header"
// info?.matchedOn → "stripe-signature"If you only need the provider name, use the lighter detectWebhookProvider():
import { detectWebhookProvider } from "@webhooks-cc/sdk";
const provider = detectWebhookProvider({ headers, body });
// → "stripe" | "github" | ... | nullUseful when you're routing a single inbound endpoint to provider-specific handlers, or logging which providers send the most traffic.
Try it without an account
The guest dashboard at webhooks.cc/go now uses real provider template payloads when you click "Send test webhook." Pick Stripe and the demo sends a properly signed checkout.session.completed. Pick GitHub and it sends a real push event. You'll see the provider badge and event name in the live dashboard before you've created an account.
What else is in this release
The same release tightens signature verification config on the server side:
- Unsupported providers are rejected at the API layer. Trying to set SendGrid as a signing provider (it uses IP allowlisting, not signatures) returns 400 with a clear message instead of silently storing the config.
- Generic HMAC requires a header name. The
signingHeaderfield is now required whensigningProvideris"generic-hmac". Saving without it returns a validation error. - Provider changes require a fresh secret. You can't change the signing provider without re-entering the secret — preventing accidental mismatches where the secret was for the old provider.
- Owner-only settings are owner-only. Team members can no longer mutate notification URLs or signing config on shared endpoints through direct API calls. The dashboard already enforced this; the API now enforces it too.
These are correctness fixes, not new features — but they close a few rough edges in the signing config path that the signature verification launch introduced.
SDK Reference
detectWebhookInfo, detectWebhookProvider, and 14 provider templates.