Skip to content
For LLMsView as Markdown·

For the operator's engineer starting a module from a generated sample.

Samples guide ​

Every sample is a complete, runnable module for one stack and one module, generated from the Operator Contract. Start from one, replace its stores with your own, and it already does the parts that are easy to get wrong.

Generated, do not edit here

The sample pages are generated from templates and the contract. They are not edited by hand in this repository, and an edit to a sample page is overwritten the next time they are generated. Copy a sample into your own repository and change it there.

What each sample contains ​

Stackwalletplayerwebhook.receivepayments.intent
TypeScript + Expresswalletplayerwebhook.receivepayments.intent
TypeScript + Fastifywalletplayerwebhook.receivepayments.intent
Python + FastAPIwalletplayerwebhook.receivepayments.intent
PHP (no framework)walletplayerwebhook.receivepayments.intent
PHP + Laravelwalletplayerwebhook.receivepayments.intent

Each wallet and player sample:

  • checks the Authorization: Bearer callback key, then verifies X-OneHazel-Signature (t=<ts>,v1=<hex>, 300 s tolerance, constant-time compare) over the raw body before parsing, then reads X-OneHazel-Env;
  • keeps a separate test ledger seeded with a funded test player, so conformance traffic never touches the live one;
  • answers errors with the flat { code, message, retryable } envelope and handles money as integer minor units;
  • for wallet, keeps an idempotency store that returns the original response with duplicate: true on a replay;
  • for player, also seeds a restricted test player.

Each webhook.receive sample verifies X-OneHazel-Signature: sha256=<hex> over the raw body, deduplicates on deliveryId, and acknowledges before doing slow work.

Each payments.intent sample is the payment status receiver: the same sha256= check, then a required X-OneHazel-Env, deduplication on deliveryId, and a settled deposit credited once per intent_id. Creating intents is a call your cashier makes to OneHazel, so no sample serves it.

The same bytes are served to a coding agent by the connect_samples MCP tool, so a person and an agent start from the same code.

Run one locally ​

Each sample page has a Run it section with the exact commands for its stack. The pattern is the same everywhere: install the framework, then start the module with the two values you were shown at registration. For a first local run, throwaway values are enough:

bash
ONEHAZEL_CALLBACK_KEY=local ONEHAZEL_SIGNING_SECRET=local npx tsx onehazel-wallet.ts
VariableUsed byMeaning
ONEHAZEL_CALLBACK_KEYwallet, playerthe callback key shown once at registration
ONEHAZEL_SIGNING_SECRETallthe signing secret shown at registration
ONEHAZEL_SIGNING_SECRET_PREVIOUSallthe old secret, during the one-hour overlap after a rotation
ONEHAZEL_TEST_PLAYER_IDwallet, playerthe test player id, default test-player-1
ONEHAZEL_RESTRICTED_TEST_PLAYER_IDplayerthe restricted test player id, default test-player-restricted
ONEHAZEL_BASE_PATHTypeScript, Python, plain PHPchanges the default /onehazel/v1 path

The Laravel samples use routes under /api/onehazel/<module> instead of a base path variable. Each Run it section names the base URL to register.

To check a local build before asking OneHazel to run, sign requests with signForTest (or signWebhookForTest) from @onehazel/connect and replay the scenarios on Conformance. Step 2 of Getting started shows a signed call with curl.

Point conformance at it ​

OneHazel runs conformance from its own servers, so the module must be reachable on a public https address. A localhost or private address is refused at registration.

  1. Expose the module on a public https URL: deploy it, or put your local server behind an https tunnel.
  2. Register that base URL for the module (connect_register_endpoint, or POST /operators/me/connect/endpoints). For wallet include testPlayerId and testGame (any provider_slug / game_code: the samples accept every game); for player include testPlayerId and restrictedPlayerId, matching the ids the sample seeds.
  3. Restart the sample with the callback key and signing secret from the registration response.
  4. Start a run (connect_run_conformance, or POST /operators/me/connect/conformance) and read the result with connect_conformance_evidence.

Before you go live ​

  • Replace the in-memory or file stores with your database. The idempotency store must survive restarts.
  • Point the live ledger at your real wallet and keep test traffic on the test ledger.
  • Keep the callback key and signing secret in your secret manager, never in the repository.