Crypto payments for casinos: an API operators can trust, and a line the platform never crosses
A B2B platform that lets gaming operators accept crypto without running blockchain infrastructure. I made its API contract something an operator can integrate without losing money, then designed the compliance layer that keeps the platform a data provider rather than a party to the transfer.
- Role
- Senior Product Manager: Operator API, Admin API, compliance
- Team
- Core engineering, a frontend agency, leadership, custody and compliance vendors
- Delivered
- Contract gap report, integration guide v1.1 and v1.2, production UAT, stage environment design, V3 compliance design
- Proves
- API and backend literacy, risk judgment, working across engineering, vendors and compliance
01Context
The client is an API-first B2B platform that lets gaming operators accept crypto deposits and pay out withdrawals (USDT, USDC and ETH on Arbitrum and Ethereum) without running any blockchain infrastructure. A regulated custodian holds the funds. The platform watches the chain, detects a deposit within seconds, keeps the ledger, and tells the operator what happened through webhooks and a REST API. The client is not named here.
I joined as Senior Product Manager. Three layers had to agree with each other: the core engine with its Operator and Admin APIs, a merchant portal and admin panel built by a frontend agency, and the custodian underneath. A live casino operator was weeks from production. Then regulation arrived on the roadmap: wallet screening, the Travel Rule and identity checks for crypto in gambling.
02Problem
User pain. An operator's engineers build against two documents: the OpenAPI spec and the integration guide. When I read them side by side, line by line, they disagreed in 24 places. Two were the kind that cost money: an operator following the guide to the letter would reserve the wrong amount for a withdrawal fee, or process a retried request twice. The rest were quieter traps: the same field called coin in requests and coinSymbol in webhooks, a reconciliation sample calling a filter the API does not have, a status described as final that is not.
Business pain. For a payments platform the integration is the product. Every hour an operator spends debugging a signature or a double credit is an hour it is not sending players, and every wrong sentence in the guide becomes a support ticket or a disputed balance. Compliance raised the stakes further: designed carelessly, it would make the platform the regulated party on every transfer.
03Approach
Contract first. I treated the live OpenAPI spec as the contract of record and diffed every statement in the guide against it: endpoints, fields, enums, fee formulas, delivery and retry behaviour. Each disagreement became either a guide fix or a written question for engineering. Anything the spec did not cover stayed an open question, never an assumption.
Rules before chapters. The guide now opens with the six rules that lose money when they are missed:
| Rule | What goes wrong without it |
|---|---|
| The crediting trigger is the operator's decision | Treating an early status as final puts the player ledger out of step with the site balance |
Deduplicate on eventId and on (txHash, logIndex), with database constraints | Retries and WebSocket duplicates double-credit the player; an application-level check loses the race |
| Verify the signature over the raw request bytes | Re-serialised JSON fails every signature, and someone "fixes" it by skipping verification |
| Never rely on webhook order | On Arbitrum a status update can arrive before the first-detection event |
| Neither failure status is final | A one-way adjustment cannot be undone when the deposit completes later |
| Integers in base units, never floats | USDT has 6 decimals and ETH 18; float arithmetic silently corrupts balances |
Test in production, without moving money. Before the operator's go-live I wrote a production UAT pack, ten flows from funding a site to settlement and exports, and ran the first pass myself on the live portal with no funds moved. It found seven issues. The one that mattered: withdrawal limits were configured per chain, while the documentation promised one limit per coin across both chains. An operator who believes a limit covers two networks when it covers one has real, avoidable exposure.
A stage environment that can reach every state. Integration tests, demos and QA all needed a held withdrawal or a reorged deposit on demand, not whenever one happened to occur. I designed Stage Control for the admin panel: a read-only catalogue of 73 scenarios, each with its expected result and its source, and a Create Transaction form that back-solves its inputs so the chosen state is genuinely reached rather than faked.
Compliance: the party before the vendor. Crypto in gambling brings wallet screening, the Travel Rule and identity checks. I compared providers slot by slot (Sumsub, Notabene and Didit among them) and wrote the deposit crediting-risk policy. But the real decision was not which vendor. It was who is the party to each transfer:
| Option | For | Against |
|---|---|---|
| The platform files the Travel Rule and stores player identity | One integration for every operator | Makes the platform the regulated party on every transfer, and an identity store it was designed not to be |
| Operators bring their own providers; the platform stays out | Nothing for the platform to license or secure | Smaller operators have no provider, and the platform has nothing to offer them |
| Operators own identity and filing in their own Sumsub account; the platform screens wallets and pushes the transfer data in | The operator stays the party; the platform adds screening and data without touching funds | Depends on the vendor's terms and written consent; where identity lives for operators with no provider is still open |
04Decision
- A guide rebuilt against the contract. Version 1.1 carried thirteen corrections, from per-chain confirmation thresholds (12 on Ethereum, 50 on Arbitrum) to a reconciliation example that finally matches the API. Version 1.2 added eight more, on native ETH and failure handling.
- One final status. Only
completedis final. Both failure statuses are reconciled daily against custody records and can still complete, so every operator-side adjustment has to be reversible. - Crediting is the operator's call. The status at which a player's balance becomes spendable is agreed per operator, never a platform default. The guide spells out what each choice exposes: crediting before
completedrisks gaming losses on unsettled value, never a direct cash-out of funds that did not arrive. - A live question, answered in a day. A live operator saw an ETH deposit sit in a failure status and asked two things: how to key a native ETH deposit that has no token log, and whether "failed" means final. I took both to engineering, came back with a real payload, and put the answers in the guide:
logIndex−1 and the zero address by convention, and daily reconciliation. The "deposit" itself turned out to be a false detection, a transfer from a new custodian wallet, which went onto the filter list. - Stage Control, designed to the contract. Every form mirrors the production endpoint it simulates, down to base units and the documented decision order: the balance is checked before the approval threshold.
- A compliance boundary written as ten guardrails. The design holds only while all ten do. Among them: no custody and no control of funds; no transfer without an authenticated operator request; the compliance layer never triggers a sale; the platform is never the Travel Rule party in its own name; no regulator submissions, only exports the operator files. And an unknown result is never shown as clean.
05Outcome
Delivered: the contract gap report (24 findings, two of them blockers) with its questions to engineering, integration guide versions 1.1 and 1.2, the production UAT pack and its first run, and answers to a live operator's integration questions within a day. Designed, not yet built: Stage Control, whose first build step waits on how the admin panel's login is trusted by the engine. Proposed, under review: the V3 compliance design, with its last open decisions sitting with leadership and counsel.
No integration-time number to show yet, and I will not invent one. What I would track: time from sandbox key to first completed deposit, integration support tickets per operator, and webhook deliveries that fail signature checks.
What surprised me. How much backend work is vocabulary. The portal says merchant and store where the engine says operator and site; the API writes confirmed_onchain where the engine writes confirmed_on_chain. Most of the expensive mistakes I found were sentences, not code.
06Reflection
Research is not counsel. I built the first compliance drafts on regulation I had read at primary sources myself. A later review corrected eleven of my own claims, from whether a threshold applies "above" or "at or above" EUR 1,000 to which article covers incoming transfers. Every regulatory line now carries "per research" until counsel answers, and next time counsel reads the first draft.
I would find the blocking question before the third prototype. Stage Control reached its third version with the question that gates all of it still open: how the admin panel's login is trusted by the engine. Now I ask "what authenticates this?" before a screen gets drawn.
And I would lock the glossary on day one. Three layers built by different teams each named things their own way, and every term I pinned late cost a round of corrections across documents.