Platform · Future
One contract,
every game
For the future, not how it works today. This is the engine room we are building for later: launch, session, round, settlement, recall and replay, with failure modes we designed before anything failed.
- Endpoints behind a round
- 8
- Licence checks per launch
- 10
- Steps in a settled bet
- 6
- Games on the same contract
- 8
two auth schemes that are never interchangeable
in one atomic read, before a URL is issued
not a single transaction, because the wallet is on somebody else's network
and it does not change shape for the next one
This is the future, not today
We are a game studio. Today our games reach players through an established server partner, and the operator keeps the player, the wallet and the licence. Everything on this page is the platform we are building for later. It exists as code and runs in development, but it is not what we sell, and no operator is connected to it.
Our titles
built and measured in the engine room below
A server you run
an established remote gaming server your team already works with
Your players
on your site, through your wallet, under your licence
Our titles reach your players through an established remote gaming server. We chose that route on purpose. Operators do not book integration dates with a studio they have not heard of, and a new studio needs shelf space first, and fast. So our games arrive the way the rest of your catalogue does, through a server your team already runs against.
This page covers what sits behind that: the engine room. Every game we have built, and every one we build next, runs on one contract: 8 endpoints, one licence chain, one settlement saga and one wallet protocol. A new title is a row in a catalogue and a directory in our repository. It does not need a second certification, a second security review or a second copy of anything. Today the contract serves 8 games, and it will not change shape for the hundredth.
It is also why a title from us is priced the way it is, and why the tenth arrives at the same quality as the first. The endpoints, licence chain, sequence and wallet protocol below are read from the same source as our own API reference, which is transcribed from the running server with a file and line number on every entry.
The contract, end to end
Eight endpoints. Two authentication schemes that are never interchangeable: the launch call is server-to-server under an operator credential, and everything else carries a session token held by a browser.
| Endpoint | Auth | What it is for | Clause |
|---|---|---|---|
| POST/v1/launch | operator | The operator's backend asks for a one-time URL to put in an iframe. Every licence question is answered here and nowhere else. | None |
| POST/v1/session/open | open | The client burns the launch ticket and receives everything it needs to draw the game truthfully: the resolved config, the market's play rules, the full description, the balance, and the commitment to the seed. | GLI-19 §4.4.1 · Ksa KS.09.28..36 (the player can read all of it before staking) |
| POST/v1/round/bet | session | One press of the button. Six steps, three of them transactions and two of them network calls into the operator's wallet. | GLI-19 §4.3.3 · UKGC RTS 14D (speed of play) · Ksa KS.09.26 (no stake while an outcome is unresolved) |
| POST/v1/round/step | session | A cash-out, a card, a pot. It stores the player's DECISIONS and never the round's state, so the engine resolves from the seed and the whole decision list every time. | GLI-19 §4.16.3(b): a game requiring player input must return to the state it was interrupted in |
| GET/v1/round/recall?limit=5 | session | The player's own recent rounds, replayable, with everything needed to check them once the seed is out. | GLI-19 §4.14 · §4.14.2 (what it must show) · §4.14.3 (the last fifty feature events) |
| POST/v1/round/resume | session | The player has been debited and the round never reached a final state. This finishes it from the ORIGINAL session's seed and nonce, under the original idempotency key. | GLI-19 §4.16.3 · Ksa KS.09.08 · KS.09.26 |
| POST/v1/session/close | session | The other half of the commitment. The server seed is published here and never before. | GLI-19 §3.2 (the commit-reveal scheme the RNG evidence rests on) |
| GET/v1/verification?fresh=1 | open | Does what is running hash to what was approved? The cached answer is free; ?fresh=1 re-hashes the deployment. | GLI-19 §2.3.2 · §B.2.6 |
Credential
Operator HMAC credential
The signature covers the scheme, the method, the path, the timestamp, the nonce and a sha256 of the body: six lines joined with newlines. The timestamp window is 30 seconds either way, the nonce is single-use, and the credential can be pinned to source addresses in credential_ip_rules. CORS deliberately never answers a preflight for /v1/launch: a browser has no business making this call, and answering would suggest otherwise.
The operator's backend. Server to server, never a browser.
Credential
Session token
An HS256 JWT, hand-rolled in thirty readable lines. It carries sid, the session id; oat, the session's opened_at in epoch milliseconds; and op, the operator id. `oat` is there because gameplay.game_sessions is partitioned by it, so a lookup without it probes every partition on the hottest read in the system (ADR-012). The header is compared byte for byte before the payload is touched, which is what closes the alg=none confusion.
The game client, in the iframe, for the life of one session.
Credential
None
Two endpoints take no credential. /v1/session/open is authenticated by the single-use launch ticket in its body rather than by a header, and burns it on first use. /v1/verification is deliberately open: it answers what the control program hashes to, which is a question a regulator or an operator's auditor is entitled to ask without being given an account.
Anyone who can reach the deployment.
Ten questions, one read, before a URL exists
Every licence question is answered by one atomic read in the database, never by the client, the session or the first bet. Because it is one read, nothing can change between two of the checks.
Operator active
The account exists and has not been suspended.
Operator's market licence
They hold a licence in the jurisdiction the player is being launched in.
Game live
The title is published rather than drafted or withdrawn.
Entitlement
This operator's licence actually includes this game. Our catalogue is not their shelf.
Contracted market
The market is one the commercial agreement covers, not merely one they hold a licence in.
Currency
The currency is one this licence is contracted in and the game declares bet levels for.
Build resolvable
A version resolves: the one they are pinned to, or the current one if they are not.
Market not blocking the build
That market's own pack does not refuse this version, which is a separate answer from whether it refuses the game.
Unexpired certificate
A laboratory certificate exists and is in date for this exact triple of build, maths profile and market. We hold none, so this is the link that refuses every real launch today, which is correct.
Campaign grant
Where the launch claims free rounds, the grant exists, belongs to this player and has not been spent.
One read, in that order, with the widest fact first so the first no is the most useful one. A refusal raises a machine code in the database’s own DETAIL field, which the server maps to an error code. Nothing branches on the message text, and no other code path in the server may answer any of these ten questions on its own.
Revocation is checked separately, each time a session loads, so a suspension takes effect on the next bet and not only at the next launch. The ticket that comes back is single-use and lives sixty seconds, and we store only its hash, so a leak of our database hands out no live launches.
The bet, in six steps
The wallet is a network call into somebody else's system. Holding one database transaction open through another company's outage is how you run out of connections, so the bet is split into short steps.
- Our databaseone short transaction, nothing in flight
round(open) + bet tx(pending) + idempotency key
Our intent, written down before anything leaves the building. The round, the bet transaction, and the client's idempotency key all land in one short transaction, under two locks taken in a fixed order.
A campaign free round runs this same step, key and bet row both, with a zero amount confirmed at insert. Skipping them used to be an optimisation, and it was a double-payout path: with no stored key a retried free round replayed nothing and consumed a second grant, and resume invented a key the wallet had never seen.
- Your walleta call out of our building and into yours
wallet debit
POST /bet into the operator's wallet, carrying our idempotency key and our transaction_id. Their answer is one of three things, and the third one is the point of the whole design.
- Our databaseone short transaction, nothing in flight
confirm / fail / leave pending
ok → the bet row is confirmed and the operator's reported balance is stored on it (§2.8.2(d) wants the funds available at the start of play, and we are not the wallet). rejected → the bet fails and the player is told why. unknown → nothing is undone: the round and its pending transaction are left in place on purpose.
Voiding on unknown would risk voiding a bet the operator actually took. Reconciliation retries the same key to find out which it was.
- The gamepure arithmetic with no clock, no I/O and no network
engine.play()
The game, resolved from the session's committed seed and the round's nonce. No I/O, no clock, no globals. Given the same stream position, bet, config and selection it produces the same result forever. That is the only reason a round from two years ago can be replayed.
A stepped game is handed the whole decision list every time and replays the round from the seed. Nothing is carried between calls, so a paused round is exactly as replayable as a spin.
- Your walleta call out of our building and into yours
win tx(pending) → credit → confirmed
Only when the round paid something. The credit's key is the bet's key with `:win` appended, so the two movements of one round are distinguishable and each is independently idempotent.
A credit that does not confirm leaves the round OPEN with a pending win. Settling here would record a win we never delivered.
- Our databaseone short transaction, nothing in flight
settle round + persist events
status settled, win_minor, draws_consumed, and every event written in one transaction. The UPDATE asserts it matched exactly one row.
That assertion is not defensive habit. A partition-key mismatch made this update match nothing and say so only by returning zero, so the API reported `settled` while the ledger kept the round open with the player already debited.
If the power goes out between any two of them
A round `open` with a `pending` transaction.
Deliberate, and detectable: ops.check_gameplay_integrity() finds them; the count is wired to an alert and shows on the World screen as `rounds_stranded`. Reconciliation retries the wallet call with the SAME idempotency key to discover what actually happened. A round left open is recoverable. A round settled on a debit that never landed is not.
The wallet is never ours, and the protocol is four calls
We hold no player funds and have nowhere to put them. Every stake and every win is a call into the operator's own wallet, keyed by a transaction id we generate. Whoever serves our games, this is how the money moves.
POST /balance
Session open, and whenever a balance has to be shown.
4 fields in · answers { "balance": "125000" }
POST /bet
A stake is taken. Always, once per round.
9 fields in · answers { "balance": "124900", "transactionRef": "your-ref-771" }
POST /win
A payout is made. Only for a round that had a /bet.
the /bet envelope + roundClosed · answers { "balance": "128300", "transactionRef": "your-ref-772" }
POST /rollback
A /bet must be undone because the round could not be completed.
7 fields in · answers { "balance": "125000" }
An answer has three arms, not two
The most important line in the contract is the boundary between rejected and unknown. A 402 is a decision. A 5xx is not, and neither is a timeout.
ok
{ status: "ok"; balanceMinor: bigint; operatorTxRef?: string }
The money moved and we know the balance afterwards.
rejected
{ status: "rejected"; reason: WalletRejection; operatorTxRef?: string }
The money definitely did NOT move. We never retry this.
unknown
{ status: "unknown"; detail: string }
We cannot tell. The round stays open, the same key is retried with backoff, and the player is not offered another spin. Collapsing this into `rejected` is how a player gets charged twice.
We never hold player funds. Every stake and every win is a call into the operator's own wallet, keyed by a transaction id we generate, and every call is idempotent on that key: a retry gets the original outcome and is never applied twice. When a server partner carries our games, the call is theirs to make, and it has to satisfy the same rule.
Treating 'unknown' as 'no' is how a player gets debited for a round that was voided, so we never do it. The round stays open with a pending transaction until reconciliation retries the same key and finds out what happened. A round left open can be recovered. A round settled on a debit that never landed cannot.
None of that asks anything unusual of a wallet. It asks for one thing that most implementations get wrong under load: answer the same key with the same outcome, and keep the caller's errors apart from your own. A 4xx is a decision; a 5xx is weather. Our reference wallet implements all four endpoints correctly and has deliberate faults you can switch on, so you can watch a conformance suite catch them before you trust it.
Three tiers, and never a branch per operator
Every 'can casino X have it different?' gets one of three answers. None of them is a fork or a new release.
Tier
1
A licence config override
Bet levels, feature toggles, the opening stake, sound and, since 2026-08-29, the skin: anything the version already declares.
Certification: None. A config key cannot reach the maths.
licensing.license_config_overrides · One row. No release, no build, no re-certification.
Tier
2
A pinned build
They want the build they tested, or they are mid-audit, or they are not ready for the new one.
Certification: The pinned build keeps its own certificate; nothing is re-tested because nothing changed.
licensing.license_games.pinned_version_id · One column. Different operators run different builds concurrently at zero engineering cost.
Tier
3
A new variant
A genuine maths or feature fork. The game is a different game.
Certification: A full pack per rung, and a laboratory certificate per market before any real-money launch.
catalog.game_variants · Its own version, its own math profiles, its own market rules, its own price.
The market’s regulatory overlay is applied last, after every operator override, using clamps instead of a plain merge. A maximum stake can only come down and a minimum interval can only go up, so an operator cannot configure their way out of a constraint. What each market clamps
What happens when something goes wrong
01
The build checks itself
Every file that can change an outcome is hashed into a release manifest. The server re-hashes them at boot and every twenty-four hours and writes the result to an append-only log.
compliance.integrity_checks: append-only, and it cannot be edited or deleted.
02
A kill switch a person turns
Stops new play platform-wide, lets rounds in flight finish, and records who turned it and why.
ops.set_gaming_enabled(false, reason)
03
An interrupted round finishes
A round left unresolved is completed using the original idempotency key read back from the bet transaction, so an attempt that already credited replays instead of paying twice. A new bet is refused while a round is unresolved.
GLI-19 §4.16.3
04
Game recall
The player can see the most recent part of their own round history, with every event as it was recorded.
GLI-19 §4.14
05
Integrity, nightly
Stranded rounds, pending transactions and orphaned events are counted every night and any non-zero count raises an alert.
ops.check_gameplay_integrity()
06
A reference wallet with deliberate defects
We ship a correct implementation of our own wallet API, with faults you can switch on, so a test house can see the conformance suite catch a fault for itself.
npm run wallet:conformance
What is not built yet, and what we are not offering yet
Nothing is deployed, and no operator is connected
There is no hosting, no TLS and no production deployment. The wallet chain runs against our own reference implementation, not a live operator sandbox. The certified client (the pinned, hashed build an operator would embed) does not exist yet. The engine room is built; the deployment is not.
We are not asking anybody to integrate with us directly
Our titles reach players through an established remote gaming server, and that is the plan, not a stopgap. A studio with no track record cannot ask an operator to book an integration date, and the fastest route to a player is the server that operator already runs. The engine room runs behind our games on that server.
A direct integration is a later product. We will build it while the catalogue earns, prove it with the brands we are closest to, and offer it once it is worth an operator’s quarter to adopt.
Two conversations we want to have
Carry the titles, or ask for them
If you run a remote gaming server or an aggregator, this is what sits behind the games you would carry. It is built to one contract, so the tenth costs you what the first did. If you are an operator, tell us which server you run and we will come to you through it. Either way, know this first: nothing is deployed yet, and no laboratory has certified anything.
hello@getawaygaming.com · Operators and aggregators can also use the enquiry form on the contact page.