{"record":{"id":"4d1d878358f387a1","repo":"openai/openai-python","slug":"the-given-webhook-signature-does-not-match-the-exp","errorCode":null,"errorMessage":"The given webhook signature does not match the expected signature","messagePattern":"The given webhook signature does not match the expected signature","errorType":"exception","errorClass":"InvalidWebhookSignatureError","httpStatus":null,"severity":"critical","filePath":"src/openai/resources/webhooks/webhooks.py","lineNumber":66,"sourceCode":"        \"\"\"Validates whether or not the webhook payload was sent by OpenAI.\n\n        Args:\n            payload: The webhook payload\n            headers: The webhook headers\n            secret: The webhook secret (optional, will use client secret if not provided)\n            tolerance: Maximum age of the webhook in seconds (default: 300 = 5 minutes)\n        \"\"\"\n        if secret is None:\n            secret = self._client.webhook_secret\n\n        if secret is None:\n            raise ValueError(\n                \"The webhook secret must either be set using the env var, OPENAI_WEBHOOK_SECRET, \"\n                \"on the client class, OpenAI(webhook_secret='123'), or passed to this function\"\n            )\n\n        if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance):\n            raise InvalidWebhookSignatureError(\n                \"The given webhook signature does not match the expected signature\"\n            ) from None\n\n\nclass AsyncWebhooks(AsyncAPIResource):\n    def unwrap(\n        self,\n        payload: str | bytes,\n        headers: HeadersLike,\n        *,\n        secret: str | None = None,\n    ) -> UnwrapWebhookEvent:\n        \"\"\"Validates that the given payload was sent by OpenAI and parses the payload.\"\"\"\n        if secret is None:\n            secret = self._client.webhook_secret\n\n        self.verify_signature(payload=payload, headers=headers, secret=secret)\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/openai/openai-python/blob/9917c6e28e66e90e1227b3d223c06a8c5441515a/src/openai/resources/webhooks/webhooks.py#L48-L84","documentation":"After resolving the secret, verify_signature recomputes the HMAC signature of the raw payload (with a timestamp, within the tolerance window) and compares it to the webhook-openai-signature header. A mismatch means the payload, headers, or secret do not correspond to what OpenAI signed, so the SDK raises InvalidWebhookSignatureError to prevent accepting forged or tampered requests. Note the 'from None' suppresses the underlying cause to avoid leaking signature details.","triggerScenarios":"Sending the wrong secret (e.g. a test secret against production webhooks), re-serializing the JSON payload instead of using the raw request bytes, missing/corrupted signature or timestamp headers, or a payload modified in transit or by middleware.","commonSituations":"Frameworks that parse and re-encode JSON before the handler sees the raw body (Express without express.raw, Next.js route handlers re-stringifying), proxy middleware altering whitespace, or simply copying the wrong secret from the dashboard.","solutions":["Use the raw request body bytes exactly as received (e.g. express.raw middleware, req.body as Buffer), not re-serialized JSON","Confirm the secret matches the webhook endpoint configured in the OpenAI dashboard","Forward the original signature and timestamp headers unmodified","If developing locally with a fake signature, call unwrap/verify with a properly computed test signature instead of hand-crafting headers"],"exampleFix":"# before (payload re-serialized -> signature mismatch)\ndata = json.loads(raw)\nclient.webhooks.unwrap(json.dumps(data).encode(), headers)\n\n# after (raw bytes preserved)\nclient.webhooks.unwrap(raw, headers)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from openai import InvalidWebhookSignatureError\n\ntry:\n    event = client.webhooks.unwrap(payload, headers)\nexcept InvalidWebhookSignatureError:\n    return Response(\"Invalid signature\", status_code=400)","preventionTips":["Verify against the raw request body bytes, never re-serialized JSON","Configure raw-body middleware before JSON parsers (e.g. express.raw)","Forward all webhook headers verbatim from your proxy","Keep server clocks NTP-synced to avoid timestamp tolerance failures","Log failures without echoing the secret or expected signature"],"tags":["webhooks","signature-verification","hmac","invalid-signature","security"],"backgroundTag":"webhook-signature-mismatch","analyzedSha":"9917c6e28e66e90e1227b3d223c06a8c5441515a","analyzedAt":"2026-08-28T11:46:34.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}