{"record":{"id":"695c0dc36b94786d","repo":"openai/openai-python","slug":"invalid-webhook-timestamp-format","errorCode":null,"errorMessage":"Invalid webhook timestamp format","messagePattern":"Invalid webhook timestamp format","errorType":"validation","errorClass":"InvalidWebhookSignatureError","httpStatus":null,"severity":"error","filePath":"src/openai/lib/_webhooks.py","lineNumber":29,"sourceCode":"\n\ndef webhook_signature_matches(\n    payload: str | bytes,\n    headers: HeadersLike,\n    *,\n    secret: str,\n    tolerance: int,\n) -> bool:\n    \"\"\"Validate the replay window and compare the supplied signatures.\"\"\"\n    signature_header = get_required_header(headers, \"webhook-signature\")\n    timestamp = get_required_header(headers, \"webhook-timestamp\")\n    webhook_id = get_required_header(headers, \"webhook-id\")\n\n    # Validate timestamp to prevent replay attacks\n    try:\n        timestamp_seconds = int(timestamp)\n    except ValueError:\n        raise InvalidWebhookSignatureError(\"Invalid webhook timestamp format\") from None\n\n    now = int(time.time())\n\n    if now - timestamp_seconds > tolerance:\n        raise InvalidWebhookSignatureError(\"Webhook timestamp is too old\") from None\n\n    if timestamp_seconds > now + tolerance:\n        raise InvalidWebhookSignatureError(\"Webhook timestamp is too new\") from None\n\n    # Extract signatures from v1,<base64> format\n    # The signature header can have multiple values, separated by spaces.\n    # Each value is in the format v1,<base64>. We should accept if any match.\n    signatures: list[str] = []\n    for part in signature_header.split():\n        if part.startswith(\"v1,\"):\n            signatures.append(part[3:])\n        else:\n            signatures.append(part)","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/openai/openai-python/blob/9917c6e28e66e90e1227b3d223c06a8c5441515a/src/openai/lib/_webhooks.py#L11-L47","documentation":"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').","triggerScenarios":"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).","commonSituations":"Proxies/LBs rewriting or dropping webhook headers; senders using ISO-8601 instead of epoch seconds; test fixtures with placeholder timestamps.","solutions":["Send the timestamp as Unix epoch seconds (e.g. '1709049600')","Log the raw webhook-timestamp header at your ingress to confirm what arrives","Ensure proxies do not mutate or strip webhook-* headers"],"exampleFix":"# before\nheaders = {\"webhook-timestamp\": \"2024-01-01T00:00:00Z\", ...}\n# after\nheaders = {\"webhook-timestamp\": \"1704067200\", ...}","handlingStrategy":"try-catch","validationCode":"def valid_ts(headers) -> bool:\n    try:\n        int(get_required_header(headers, \"webhook-timestamp\"))\n        return True\n    except (ValueError, KeyError):\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    ok = webhook_signature_matches(body, headers, secret)\nexcept InvalidWebhookSignatureError as e:\n    if \"timestamp format\" in str(e):\n        return Response(400)\n    raise","preventionTips":["Send webhook timestamps as epoch seconds","Verify proxies pass webhook-* headers unmodified","Log raw headers for incoming webhooks"],"tags":["webhooks","validation","signature"],"backgroundTag":"webhook-signature-invalid","analyzedSha":"9917c6e28e66e90e1227b3d223c06a8c5441515a","analyzedAt":"2026-08-28T11:46:34.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}