ComposioHQ/composio · error · ComposioWebhookPayloadError

Invalid webhook timestamp: ${webhookTimestamp}. Expected Uni

Error message

Invalid webhook timestamp: ${webhookTimestamp}. Expected Unix timestamp in seconds.

What it means

The 'webhook-timestamp' header could not be parsed as an integer Unix timestamp in seconds (parseInt yields NaN). The timestamp is needed both for the HMAC payload and for the replay-tolerance check, so a non-numeric value is rejected with ComposioWebhookPayloadError before any crypto work.

Source

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

      }
    }

    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.`
      );
    }

    const webhookTimeMs = timestampSeconds * 1000;
    const currentTime = Date.now();
    const timeDifference = Math.abs(currentTime - webhookTimeMs);

    if (timeDifference > tolerance * 1000) {
      throw new ComposioWebhookSignatureVerificationError(
        `The webhook timestamp is outside the allowed tolerance. ` +
          `The webhook was sent ${Math.round(timeDifference / 1000)} seconds ago, ` +
          `but the maximum allowed age is ${tolerance} seconds.`
      );
    }
  }
}

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Pass the header value through unmodified — Composio always sends epoch seconds as a string
  2. In tests, generate Math.floor(Date.now()/1000).toString()
  3. Return 400 early for requests whose webhook-timestamp is not a digit-only string
  4. Check for middleware that rewrites header values

Example fix

// before
const ts = new Date().toISOString(); // wrong format
// after
const ts = Math.floor(Date.now() / 1000).toString();
Defensive patterns

Strategy: validation

Validate before calling

const ts = String(req.headers['webhook-timestamp'] ?? '');
if (!/^\d+$/.test(ts)) return res.status(400).send('Bad webhook-timestamp');

Type guard

const isUnixSeconds = (v: unknown): v is string => typeof v === 'string' && /^\d{10}$/.test(v);

Try / catch

try { verifyWebhookSignature(...); } catch (e) { if (e instanceof ComposioWebhookPayloadError) return res.status(400).end(); throw e; }

Prevention

When it happens

Trigger: Passing a timestamp like '2024-01-01T00:00:00Z', an ISO string, milliseconds with a trailing unit ('1700000000000ms'), an empty-ish/garbage string that survived the earlier length check, or a value corrupted by header encoding.

Common situations: Test fixtures using ISO dates instead of epoch seconds, transforming the header before verification, or non-Composio clients sending arbitrary header values to your endpoint.

Related errors


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