--- title: "Connect: conformance" description: The scenarios OneHazel runs against each Operator Contract module before it can go live, with severity and what passing looks like. --- # Conformance Before a module counts as `conformant`, OneHazel runs a fixed set of scenarios against the base URL you registered. Every wallet and player call is signed, carries `X-OneHazel-Env: test`, and uses only the test player you named; webhook deliveries are signed `sha256=` exactly as live ones are. A **blocker** failing keeps the module from going live; a **major** must be fixed before the first real vendor; a **minor** is advice. Your coding agent can read the same list with `connect_conformance_scenarios` before writing any code. A run that has not finished is "not yet verified", never a pass. In `connect_status`, `evidence: derived` means no conformance run backs this module; in v1 that is `launch`, `widget`, `attribution` and `events.push`. ## wallet | Scenario | Severity | Passing looks like | |---|---|---| | `auth.bad_bearer` | blocker | 401 with the error envelope; no wallet change | | `auth.bad_signature` | blocker | 401; the body is verified before it is parsed | | `auth.stale_timestamp` | blocker | 401 when the timestamp is more than 300 s off, even with a valid MAC | | `balance.ok` | blocker | 200 with an integer `balance_minor` and the player's currency | | `bet.ok` | blocker | 200, `status: accepted`, balance reduced by exactly `amount_minor` | | `win.ok` | blocker | 200, `status: accepted`, balance increased by exactly `amount_minor` | | `bet.insufficient_funds` | blocker | 402 `insufficient_funds`; the wallet is not touched | | `bet.duplicate_returns_original` | blocker | the ORIGINAL response with `duplicate: true` and no second debit | | `rollback.bet` | blocker | `status: rolled_back`, stake returned by your stored amount | | `rollback.win` | blocker | `status: rolled_back`, credit reversed by your stored amount | | `rollback.amount_mismatch` | major | `rollback_amount_mismatch`; nothing reversed | | `freerounds.bet_no_debit` | major | no `wallet/bet` for a free-round bet; balance unchanged until the win | | `freerounds.settle_lump` | major | `freerounds/settle` credits the lump itself, never alongside a `wallet/win` | | `env.test_header_required` | blocker | `test` traffic touches only the test player or sandbox ledger | | `shape.empty_body_is_failure` | blocker | every success matches the contract schema; a bare 2xx is a failure | ## player | Scenario | Severity | Passing looks like | |---|---|---| | `lookup.ok` | blocker | 200 with `player_external_id`, `currency`, `country`, `status` and a `restrictions` array | | `lookup.unknown_404` | major | 404 with the error envelope, never a 200 with empty fields | | `lookup.restricted_flag` | major | `restrictions` lists the active restriction; empty only when none | ## webhook.receive | Scenario | Severity | Passing looks like | |---|---|---| | `deliver.ok` | blocker | 2xx after verifying `X-OneHazel-Signature: sha256=` over the raw body (no timestamp, no bearer, no env header) | | `deliver.replay_ignored` | major | 2xx both times for the same `deliveryId`, processed once | | `deliver.tampered_rejected` | blocker | non-2xx for a body altered after signing | | `deliver.timeout_5s` | major | the 2xx arrives within 5 s; slow work runs after acknowledging | ## Check it yourself first The generated samples are built to these scenarios. To exercise your own build before asking OneHazel to run, sign requests with `signForTest` from [`@onehazel/connect`](/connect/contract#helper) and replay the table above against your local server. ## See also - Per-module guides: [wallet](/connect/modules/wallet), [player](/connect/modules/player), [webhook.receive](/connect/modules/webhook-receive). - [Troubleshooting](/connect/troubleshooting) for failed scenarios and errored runs.