openai/openai-python · error · InvalidWebhookSignatureError

Invalid webhook timestamp format

Error message

Invalid webhook timestamp format

What it means

webhook_signature_matches validates signed webhook payloads. The webhook-timestamp header must be an integer Unix timestamp; a non-numeric value raises InvalidWebhookSignatureError('Invalid webhook timestamp format').

Source

Thrown at src/openai/lib/_webhooks.py:29


def webhook_signature_matches(
    payload: str | bytes,
    headers: HeadersLike,
    *,
    secret: str,
    tolerance: int,
) -> bool:
    """Validate the replay window and compare the supplied signatures."""
    signature_header = get_required_header(headers, "webhook-signature")
    timestamp = get_required_header(headers, "webhook-timestamp")
    webhook_id = get_required_header(headers, "webhook-id")

    # Validate timestamp to prevent replay attacks
    try:
        timestamp_seconds = int(timestamp)
    except ValueError:
        raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None

    now = int(time.time())

    if now - timestamp_seconds > tolerance:
        raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None

    if timestamp_seconds > now + tolerance:
        raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None

    # Extract signatures from v1,<base64> format
    # The signature header can have multiple values, separated by spaces.
    # Each value is in the format v1,<base64>. We should accept if any match.
    signatures: list[str] = []
    for part in signature_header.split():
        if part.startswith("v1,"):
            signatures.append(part[3:])
        else:
            signatures.append(part)

View on GitHub (pinned to 9917c6e28e)

Solutions

  1. Send the timestamp as Unix epoch seconds (e.g. '1709049600')
  2. Log the raw webhook-timestamp header at your ingress to confirm what arrives
  3. Ensure proxies do not mutate or strip webhook-* headers

Example fix

# before
headers = {"webhook-timestamp": "2024-01-01T00:00:00Z", ...}
# after
headers = {"webhook-timestamp": "1704067200", ...}
Defensive patterns

Strategy: try-catch

Validate before calling

def valid_ts(headers) -> bool:
    try:
        int(get_required_header(headers, "webhook-timestamp"))
        return True
    except (ValueError, KeyError):
        return False

Try / catch

try:
    ok = webhook_signature_matches(body, headers, secret)
except InvalidWebhookSignatureError as e:
    if "timestamp format" in str(e):
        return Response(400)
    raise

Prevention

When it happens

Trigger: A request where the webhook-timestamp header is missing-numeric content (e.g. an ISO date string, empty, or corrupted by a proxy that rewrites headers).

Common situations: Proxies/LBs rewriting or dropping webhook headers; senders using ISO-8601 instead of epoch seconds; test fixtures with placeholder timestamps.

Related errors


AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28). Data as JSON: /api/errors/695c0dc36b94786d. Report an issue: GitHub.