ComposioHQ/composio · critical · ComposioWebhookSignatureVerificationError
The signature provided is invalid. Please ensure you are usi
Error message
The signature provided is invalid. Please ensure you are using the correct webhook secret.
What it means
The computed HMAC-SHA256(msgId.timestamp.payload, secret) does not match any of the v1 signatures supplied in the 'webhook-signature' header. This means the payload was signed with a different secret, or the payload/id/timestamp/secret inputs differ from what was signed. It is the core anti-tamper check of webhook verification.
Source
Thrown at ts/packages/core/src/models/Triggers.ts:1290
"Expected format: 'v1,base64EncodedSignature'"
);
}
// Compute expected signature: HMAC-SHA256(msgId.timestamp.payload, secret) -> base64
const toSign = `${webhookId}.${webhookTimestamp}.${payload}`;
const expectedSignature = await hmacSha256Base64(secret, toSign);
// Check if any of the provided signatures match
let isValid = false;
for (const providedSignature of v1Signatures) {
if (timingSafeEqual(providedSignature, expectedSignature)) {
isValid = true;
break;
}
}
if (!isValid) {
throw new ComposioWebhookSignatureVerificationError(
'The signature provided is invalid. Please ensure you are using the correct webhook secret.'
);
}
}
/**
* Validates that the webhook timestamp is within the allowed tolerance
* @private
*/
private validateWebhookTimestamp(webhookTimestamp: string, tolerance: number): void {
const timestampSeconds = parseInt(webhookTimestamp, 10);
if (Number.isNaN(timestampSeconds)) {
throw new ComposioWebhookPayloadError(
`Invalid webhook timestamp: ${webhookTimestamp}. Expected Unix timestamp in seconds.`
);
}
View on GitHub (pinned to 64b1b85502)
Solutions
- Re-copy the webhook secret from the Composio dashboard and confirm it matches the environment variable used
- Verify against the RAW request body bytes (e.g. express.raw before JSON parsing), not a re-serialized object
- Ensure webhookId and webhookTimestamp come from the same delivery as the payload
- Confirm you are not mixing secrets across workspaces/environments
Example fix
// before (express, body already parsed)
app.post('/webhook', express.json(), (req, res) => {
verifyWebhookSignature(JSON.stringify(req.body), ...);
});
// after (raw body)
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
verifyWebhookSignature(req.body.toString('utf8'), req.headers['webhook-signature'], {
secret: process.env.COMPOSIO_WEBHOOK_SECRET,
webhookId: req.headers['webhook-id'],
webhookTimestamp: req.headers['webhook-timestamp'],
});
}); Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
verifyWebhookSignature(rawBody, sig, { secret, webhookId, webhookTimestamp });
} catch (e) {
if (e instanceof ComposioWebhookSignatureVerificationError) {
// Do NOT process the payload; 400/401 and alert — possible forgery or secret drift
return res.status(401).end();
}
throw e;
} Prevention
- Always verify the raw request body bytes, not re-serialized JSON
- Load the webhook secret from a single env var kept in sync with the dashboard
- Rotate the secret with a transition period supporting old and new secrets
- Use matched id/timestamp/body from the same delivery
When it happens
Trigger: Using the wrong webhook secret (regenerated in the dashboard, wrong environment/project key), verifying a modified or re-serialized body (JSON re-stringified so bytes differ), mixing the id/timestamp from one delivery with the body of another, or receiving a forged/spoofed request.
Common situations: Rotating the webhook secret in the dashboard without updating the env var, verifying req.body (parsed then re-stringified) instead of the raw request body, secret copied with whitespace/quotes, or multiple Composio workspaces with different secrets.
Related errors
- UnsafeZipEntryError: ${entry.fileName}
- Invalid parameters passed to set webhook subscription
- No webhook ID was provided. Please pass the value of the 'we
- No webhook timestamp was provided. Please pass the value of
- No valid v1 signature found in the webhook-signature header.
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/e051f9d0b25476a7.
Report an issue: GitHub.