Casino Integration — Seamless Wallet API
This is the contract for the wallet API your casino implements and the prediction-markets platform calls to move money. Your casino remains the single source of truth for the player's balance; we never hold funds. When a player bets, we debit you; when they win or a market is cancelled, we credit you; if a bet fails after we debited, we roll back.
- Direction: platform → casino (outbound). These endpoints live under your
base URL (the
outboundBaseUrlyou give us at onboarding). - Auth: every call carries a platform
private_key_jwtbearer token. Verify it exactly as described in outbound-auth.md. - Machine spec:
openapi/casino-integration.yaml.
Conventions
- Money is always integer minor units (cents) in the field
amountCents. - Currency is
EURfor all operations (sent explicitly on every call). - Player id (
playerId) is the casino's own player identifier — the sameexternalIdcarried assubin the player tokens your backend issues to us. - Content type is
application/json; responses should be JSON.
Idempotency — the one rule that matters
Every money operation carries a txRef: the platform's idempotency
reference, unique per tenant. Your implementation must guarantee:
Applying the same
txReftwice has the same effect as applying it once.
Concretely:
- A repeated
debit/creditwith atxRefyou've already processed must return the original result (HTTP 200) without moving money again. - A
rollbackfor anoriginalTxRefyou've already reversed (or never saw) must be a safe no-op returning success.
This is what makes our retries safe: on a network blip or 5xx we retry the same
txRef, and your idempotency guarantees no double charge. Store txRef with a
uniqueness constraint and check it before applying.
Endpoints
POST /wallet/debit — reserve a bet stake
Request:
{
"txRef": "1f6c…",
"playerId": "player-9",
"amountCents": 1500,
"currency": "EUR",
"context": { "betId": "…", "marketId": "…", "outcomeId": "…" }
}
Success (200):
{ "transactionId": "casino-tx-1", "balanceCents": 8000 }
Both response fields are optional but recommended — we store transactionId for
reconciliation and surface balanceCents where useful.
If the player can't cover the stake, reject with either:
400and body{ "code": "INSUFFICIENT_FUNDS", "message": "…" }, or402.
We treat both as a clean bet rejection (the player simply can't afford it) — no rollback, no error escalation.
POST /wallet/credit — payout or refund
Same request/response shape as debit. Used to pay a winning bet at resolution
or refund a stake when a market is cancelled.
POST /wallet/rollback — reverse a debit
Request:
{
"txRef": "9ab2…",
"originalTxRef": "1f6c…",
"playerId": "player-9",
"amountCents": 1500,
"currency": "EUR"
}
Reverses the debit identified by originalTxRef. Returns 200 with an optional
{ "transactionId", "balanceCents" }. Must be idempotent and a safe no-op if the
debit is unknown or already reversed.
GET /wallet/balance?player=<playerId> — read balance (optional)
{ "balanceCents": 8000 }
A convenience for preflight checks. Not required for correctness.
Error codes
| HTTP | code |
Meaning | Platform reaction |
|---|---|---|---|
| 200 | — | Applied (or idempotent replay) | Proceed |
| 400 | INSUFFICIENT_FUNDS |
Player can't cover the debit | Reject the bet cleanly |
| 402 | — | Insufficient funds (alternative to the 400 code) | Reject the bet cleanly |
| 400/409 | other | Definitive rejection | Fail the operation, not retried |
| 5xx | — | Transient server error | Retried (bounded), then failed |
Network errors and timeouts are treated like 5xx — retried, then surfaced as a transient failure.
How the platform uses these (so you can reason about ordering)
A bet uses a debit-first saga:
debitthe stake (amountCents) withtxRef.INSUFFICIENT_FUNDS/402→ the bet is rejected; nothing else happens.
- The platform places the bet internally.
- If placement fails (e.g. price moved beyond tolerance, market just closed),
the platform issues a
rollbackof that debit'stxRef.
At market settlement:
- Resolution → a
creditto each winning player for their payout. - Cancellation → a
creditrefunding each open bet's stake.
Settlement credits are delivered by a background worker with retries, so expect
that a credit may arrive shortly after the market resolves and may be retried
with the same txRef until you acknowledge it with a 200.
Reconciliation
Return a stable transactionId on every successful operation. The platform keeps
a ledger keyed by txRef ↔ your transactionId, which is what both sides use to
reconcile disputes and the monthly settlement.
Reference implementation
A runnable reference of this wallet lives at
examples/casino-integration/ — the
server-side analogue of ws-test.html. casino-wallet-stub.mjs verifies our
token and implements debit/credit/rollback/balance with txRef
idempotency; run-smoke.ts drives the real platform against it end to end. See
the example README to run it.