ComposioHQ/composio · error · ComposioWebhookSignatureVerificationError

No webhook timestamp was provided. Please pass the value of

Error message

No webhook timestamp was provided. Please pass the value of the 'webhook-timestamp' header.

What it means

Thrown when the 'webhook-timestamp' header is missing or empty during webhook signature verification. The timestamp is a component of the signed payload (msgId.timestamp.payload) and is also used for replay-attack tolerance checks, so it is mandatory. The library throws ComposioWebhookSignatureVerificationError rather than guessing a timestamp.

Source

Thrown at ts/packages/core/src/models/Triggers.ts:1253

      throw new ComposioWebhookSignatureVerificationError(
        "No signature header value was provided. Please pass the value of the 'webhook-signature' header."
      );
    }

    if (secret.length === 0) {
      throw new ComposioWebhookSignatureVerificationError(
        'No webhook secret was provided. You can find your webhook secret in your Composio dashboard.'
      );
    }

    if (webhookId.length === 0) {
      throw new ComposioWebhookSignatureVerificationError(
        "No webhook ID was provided. Please pass the value of the 'webhook-id' header."
      );
    }

    if (webhookTimestamp.length === 0) {
      throw new ComposioWebhookSignatureVerificationError(
        "No webhook timestamp was provided. Please pass the value of the 'webhook-timestamp' header."
      );
    }

    // Parse signature - may have multiple signatures prefixed with version (e.g., "v1,base64sig")
    const signatures = signature.split(' ');
    const v1Signatures: string[] = [];

    for (const sig of signatures) {
      const [version, value] = sig.split(',');
      if (version === 'v1' && value) {
        v1Signatures.push(value);
      }
    }

    if (v1Signatures.length === 0) {
      throw new ComposioWebhookSignatureVerificationError(
        'No valid v1 signature found in the webhook-signature header. ' +

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Read and pass the 'webhook-timestamp' header exactly as received from Composio
  2. Verify header names survive any proxy/gateway in front of your handler
  3. Reject or 400 non-Composio requests before calling verification
  4. In tests, generate a current Unix-seconds timestamp header value

Example fix

// before
verify({ signature: req.headers['webhook-signature'], webhookId: req.headers['webhook-id'], webhookTimestamp: '' });
// after
verify({
  signature: req.headers['webhook-signature'],
  webhookId: req.headers['webhook-id'],
  webhookTimestamp: req.headers['webhook-timestamp'],
});
Defensive patterns

Strategy: validation

Validate before calling

const ts = req.headers['webhook-timestamp'];
if (typeof ts !== 'string' || ts.length === 0) {
  return res.status(400).send('Missing webhook-timestamp header');
}

Type guard

const isNonEmptyHeader = (v: unknown): v is string => typeof v === 'string' && v.length > 0;

Try / catch

try { verifyWebhookSignature(...); } catch (e) { if (e instanceof ComposioWebhookSignatureVerificationError) { logger.warn('webhook rejected', e.message); return res.status(400).end(); } throw e; }

Prevention

When it happens

Trigger: Calling verifyWebhookSignature with webhookTimestamp set to '' — e.g. the 'webhook-timestamp' header was not read from the request, was renamed by the framework, or the request genuinely lacks it (very old webhook deliveries or non-Composio traffic hitting the endpoint).

Common situations: Header-name casing mismatches (Node lowercases, some platforms do not), proxies stripping custom headers, or sending the webhook endpoint a manually crafted/test payload without the Composio headers.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/0db859557f02220e. Report an issue: GitHub.