Repository navigation
feat: first-class helper for Standard Webhooks (Svix) signature verification #4928
Description
Title : feat: first-class helper for Standard Webhooks (Svix) signature verification
Labels : enhancement
Is your feature request related to a problem? Please describe.
I'm building event-driven automations where Trigger.dev jobs react to webhook events emitted by Composio (and a growing number of other providers that have adopted the Standard Webhooks spec — Stripe, GitHub, Slack, Linear, etc.). Composio sends three headers (webhook-id, webhook-timestamp, webhook-signature: v1,<base64>) and the signature is HMAC-SHA256 over ${id}.${timestamp}.${raw_body} with the secret base64-decoded and a tolerance window (default 300s).
The built-in verifyRequestSignature helper only covers single-header schemes (Stripe/GitHub/Cal.com/Novu style), so it can't validate Standard Webhooks signatures out of the box. I get why — supporting every provider's signature scheme isn't Trigger.dev's job, and the helper is the right escape hatch for ad-hoc cases.
The friction isn't the helper itself; it's that every Trigger.dev user integrating with a Standard Webhooks provider ends up writing the same ~50 lines of crypto + header parsing in their verify lambda. That's the part that feels like it could live in the SDK.
Describe the solution you'd like to see
A first-class helper that handles the Standard Webhooks signature scheme, roughly:
import { verifyStandardWebhooks, STANDARD_WEBHOOKS_TOLERANCE_SECONDS } from "@trigger.dev/sdk";
const endpoint = client.defineHttpEndpoint({
id: "composio",
source: "composio.dev",
verify: async (request) => {
return await verifyStandardWebhooks({
request,
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
// optional, defaults to 300s per the Standard Webhooks spec
tolerance: STANDARD_WEBHOOKS_TOLERANCE_SECONDS,
});
},
});What the helper should cover:
- Read
webhook-id,webhook-timestamp,webhook-signatureheaders - Validate the
v1,signature prefix (and tolerate futurev1a,/v1b,variants if the spec adds them) - Base64-decode the secret before HMAC
- Compute
HMAC-SHA256(secret, ${id}.${timestamp}.${raw_body})withcrypto.timingSafeEqual - Enforce the timestamp tolerance window (anti-replay)
- Return the same
{ success: true }/{ success: false, reason: "..." }shape asverifyRequestSignatureso it's a drop-in
Bonus if it also exposes the parsed, verified payload (mirroring what the Composio SDK does on its verifyWebhook return type) — that lets users skip the manual JSON.parse after success: true.
Describe alternate solutions
I considered a few:
- PR my own implementation into the SDK. I can do this, but the vouched-contributor + changesets + single-feature-per-PR process makes it heavier than a feature request first. And honestly I'd rather the maintainers pick the API shape —
verifyStandardWebhooksvs. a genericverifyWebhook({ scheme: "standard-webhooks", ... })flag, where in the helper tree it lives, etc. - Publish a separate community package (
@trigger-dev-community/standard-webhooks-verify). Viable, but the moment Trigger.dev ships its own helper, the community package dies. Putting it upstream also makes it discoverable next toverifyRequestSignature. - Move the verification outside Trigger.dev. A small service in front of Trigger.dev that does the verification + republishes to a queue that Trigger.dev jobs subscribe to. Works, but it's exactly the kind of workaround that disappears when the gap closes. I'd rather not maintain it if I don't have to — and it pushes trust boundaries out of the SDK, which is the opposite of where they belong.
Additional information
Adoption is broader than it looks. Standard Webhooks isn't a niche format — it's becoming the de facto signature spec for SaaS webhooks:
- Big-name migrations to Standard Webhooks: Stripe, GitHub, Slack, Linear, Shopify (via Svix)
- Svix-native providers: most new B2B SaaS that use Svix as their delivery layer (Resend, Novu, Knock, Courier, ngrok, Composio, etc.)
- Spec is actively maintained: the Standard Webhooks org publishes reference SDKs in Node, Python, Go, Ruby, Java, .NET, Rust, PHP — so wrapping one of those in a Trigger.dev helper is low-risk and well-tested upstream crypto
That means a single helper in the SDK would unblock integrations with every provider in the list above — not just one. The same ~50 lines of crypto currently written by hand per integration become write-once-for-everyone.
Why now : the Standard Webhooks spec keeps winning — Stripe and GitHub both migrated to it; Linear, Svix-hosted providers, and a long tail of "send us webhooks" SaaS follow. Every new Standard Webhooks integration in Trigger.dev costs ~50 lines of boilerplate that the SDK could own.
I'm not blocked on this — my workaround works — but it felt worth flagging since the cost of closing the gap is low and the gain compounds with every new Standard Webhooks integration anyone tries to build.
Spec references :
- https://www.standardwebhooks.com/ — the spec
- https://docs.composio.dev/docs/webhook-verification — provider example that uses it
- http://github.com/standard-webhooks/standard-webhooks — reference implementations in multiple languages
Activity