{"record":{"id":"038c15d02fb761f4","repo":"ComposioHQ/composio","slug":"no-valid-v1-signature-found-in-the-webhook-signatu","errorCode":null,"errorMessage":"No valid v1 signature found in the webhook-signature header. Expected format: 'v1,base64EncodedSignature'","messagePattern":"No valid v1 signature found in the webhook-signature header\\. Expected format: 'v1,base64EncodedSignature'","errorType":"exception","errorClass":"ComposioWebhookSignatureVerificationError","httpStatus":null,"severity":"error","filePath":"ts/packages/core/src/models/Triggers.ts","lineNumber":1270,"sourceCode":"    if (webhookTimestamp.length === 0) {\n      throw new ComposioWebhookSignatureVerificationError(\n        \"No webhook timestamp was provided. Please pass the value of the 'webhook-timestamp' header.\"\n      );\n    }\n\n    // Parse signature - may have multiple signatures prefixed with version (e.g., \"v1,base64sig\")\n    const signatures = signature.split(' ');\n    const v1Signatures: string[] = [];\n\n    for (const sig of signatures) {\n      const [version, value] = sig.split(',');\n      if (version === 'v1' && value) {\n        v1Signatures.push(value);\n      }\n    }\n\n    if (v1Signatures.length === 0) {\n      throw new ComposioWebhookSignatureVerificationError(\n        'No valid v1 signature found in the webhook-signature header. ' +\n          \"Expected format: 'v1,base64EncodedSignature'\"\n      );\n    }\n\n    // Compute expected signature: HMAC-SHA256(msgId.timestamp.payload, secret) -> base64\n    const toSign = `${webhookId}.${webhookTimestamp}.${payload}`;\n    const expectedSignature = await hmacSha256Base64(secret, toSign);\n\n    // Check if any of the provided signatures match\n    let isValid = false;\n    for (const providedSignature of v1Signatures) {\n      if (timingSafeEqual(providedSignature, expectedSignature)) {\n        isValid = true;\n        break;\n      }\n    }\n","sourceCodeStart":1252,"sourceCodeEnd":1288,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/core/src/models/Triggers.ts#L1252-L1288","documentation":"The 'webhook-signature' header must contain at least one signature in the versioned format 'v1,<base64HMAC>'. The parser splits entries and keeps those whose version prefix is 'v1' with a non-empty value; if none qualify, this ComposioWebhookSignatureVerificationError is thrown. This catches malformed or future-versioned signature headers before any HMAC comparison.","triggerScenarios":"Passing a signature header that is raw base64 without the 'v1,' prefix, an empty string, a wrong scheme like 'sha256=...', or only unsupported version prefixes (e.g. 'v2,...').","commonSituations":"Copy-pasting verification code from Svix/Stripe-style schemes (sha256=), hand-crafting test signatures, or a provider version change that alters the signature header format.","solutions":["Send/forward the header verbatim from Composio: 'v1,' + base64(HMAC-SHA256(msgId.timestamp.payload, secret))","If computing signatures yourself (tests), prefix the base64 HMAC with 'v1,'","Do not parse/reformat the header before passing it to the SDK","Check for SDK/backend version drift if Composio now sends a different version prefix"],"exampleFix":"// before (test helper)\nconst sig = crypto.createHmac('sha256', secret).update(msg).digest('base64');\n// after\nconst sig = 'v1,' + crypto.createHmac('sha256', secret).update(`${msgId}.${ts}.${payload}`).digest('base64');","handlingStrategy":"validation","validationCode":"const sig = String(req.headers['webhook-signature'] ?? '');\nconst hasV1 = sig.split(' ').some(p => { const [v, val] = p.split(','); return v === 'v1' && !!val; });\nif (!hasV1) return res.status(400).send('Bad signature header');","typeGuard":"const hasV1Signature = (sigHeader: unknown): sigHeader is string =>\n  typeof sigHeader === 'string' &&\n  sigHeader.split(' ').some(part => { const [v, val] = part.split(','); return v === 'v1' && !!val; });","tryCatchPattern":"try { verifyWebhookSignature(...); } catch (e) { if (e instanceof ComposioWebhookSignatureVerificationError) return res.status(400).end(); throw e; }","preventionTips":["Never hand-build the signature header; use values produced by Composio or a correct 'v1,'+base64 HMAC in tests","Reject requests whose signature header lacks a v1 entry before invoking verification","Pin SDK versions in tests that compute signatures"],"tags":["webhook","signature-verification","malformed-header","typescript"],"backgroundTag":"webhook-signature-verification-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}