--- title: "Connect module: webhook.receive" description: The endpoint OneHazel delivers events to. The sha256 signature, deduplication on deliveryId, conformance scenarios and samples. --- For the operator's engineer building the webhook.receive module. # webhook.receive The webhook.receive module is where OneHazel delivers events to you: KYC verdicts, responsible-gaming actions, payment outcomes and the like. It uses OneHazel's existing webhook delivery format unchanged, so a receiver you already run for OneHazel webhooks keeps working. ## Who calls whom OneHazel calls you, at `POST /webhook` under the base URL you register for `webhook.receive`. The body carries `type`, `data`, `operatorId`, `timestamp` and `deliveryId`. ## Headers and signature This module uses a **different** signature from wallet and player, on purpose: | Header | Value | |---|---| | `X-OneHazel-Signature` | `sha256=`, HMAC-SHA256 of the **raw** body keyed by your `whsec_` signing secret. No timestamp. | | `X-OneHazel-Event` | the same value as body `type` | | `X-OneHazel-Delivery` | the same value as body `deliveryId` | There is no bearer. Live deliveries carry no `X-OneHazel-Env`; conformance deliveries add `X-OneHazel-Env: test`, so do not reject a delivery for having or lacking it. Verify the signature over the raw bytes before parsing and compare in constant time. Answer `401` on a mismatch. ## Replays and timing With no timestamp, a replayed delivery verifies. Deduplicate on `deliveryId`: that is the replay defence. Deliveries are at-least-once, so the same `deliveryId` can arrive twice; answer 2xx both times and process it once. Answer 2xx within 5 seconds and do slow work after acknowledging. Anything else, including a timeout, is retried on the subscription's retry policy. ## Conformance | Scenario | Severity | Passing looks like | |---|---|---| | `deliver.ok` | blocker | 2xx for a correctly signed delivery | | `deliver.replay_ignored` | major | 2xx both times for the same `deliveryId` | | `deliver.tampered_rejected` | blocker | non-2xx for a body altered after signing | | `deliver.timeout_5s` | major | the 2xx arrives within 5 s | ## Samples | Stack | Sample | |---|---| | TypeScript + Express | [webhook.receive](/connect/samples/typescript/express/webhook.receive) | | TypeScript + Fastify | [webhook.receive](/connect/samples/typescript/fastify/webhook.receive) | | Python + FastAPI | [webhook.receive](/connect/samples/python/fastapi/webhook.receive) | | PHP (no framework) | [webhook.receive](/connect/samples/php/plain/webhook.receive) | | PHP + Laravel | [webhook.receive](/connect/samples/php/laravel/webhook.receive) | The TypeScript helper `@onehazel/connect` exports `verifyWebhookSignature` for this format; see [the contract](/connect/contract#helper).