Invalid webhook signature
1 min read
Signature verification failed and the payload was rejected before anything was written.
Why this check is not optional
The Linear webhook endpoint is public because it has to be — Linear sends no bearer token. The signature is the entire authentication, which makes this the most security-relevant check in the dispatch path.
An earlier version read if (signature && workspace.webhookSecret), which meant
omitting the header skipped verification entirely. Anyone who learned a
Linear organisation id — not a secret; it appears in payloads — could forge a
dispatch into somebody else's fleet. A missing header and a missing stored secret
are each a rejection now.
The three usual causes
Verifying against the wrong bytes. The signature covers the raw request body. Parsing to JSON and re-serialising produces different bytes — key order, whitespace, unicode escaping — and the HMAC will not match. Read the raw body first and verify against that exact string.
A stale or mismatched secret. The secret stored at connection time no longer matches what Linear is signing with, usually because the webhook was recreated in Linear without reconnecting here.
A replayed or altered request. Payloads older than sixty seconds are refused regardless of signature, which blunts replay of a captured request.
How to fix it
Reconnect the workspace from Settings → Linear so a fresh secret is stored, and confirm the webhook in Linear points at the current URL. If you are implementing this yourself, check that you are hashing the raw body rather than a re-encoded copy — that one accounts for most of these.
