Create a real-money session
POST/sessions
Create a real-money game session. The player is redirected to the
returned game_url to begin playing. The URL expires in 20 seconds -
create the session immediately before redirecting.
Requires Idempotency-Key header. Retries with the same key within
24 hours return the cached response, replayed with its original
status and an X-Idempotent-Replayed: true header. This endpoint
does not compare request bodies: a key reused with a different body
replays the original response rather than returning 409, except for
Mobule: a cached launcher is reauthorized against the persisted session’s
operator, organization, environment, normalized game/player and current
support approval. Platform keys must retry with the same
sub_operator_ref, or omit it only if the launch omitted it; a different
or unregistered ref returns 403 and is never provisioned on a replay. A
conflicting or revoked Mobule retry returns 403; authorization storage
uncertainty returns 503. Every successful replay reads session storage, so
missing/unavailable sessions can return 503 for any provider. A new Mobule
access denial releases the reservation, permitting the same key to be
retried after approval.
Mobule requires current support approval for the exact organization bound
to the authenticated operator key. Unbound keys cannot infer an organization.
For a platform key, the approval of the platform’s organization covers
its brands; a suspended or retired brand is refused.
Discovery omits unapproved games (direct game/stakes lookup returns 404).
Launches and launcher re-reads return PROVIDER_ACCESS_REQUIRED (403) on
denial; storage uncertainty returns PROVIDER_ACCESS_UNAVAILABLE (503).
Requests for other providers keep their existing access policy.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Unique key for idempotent retry semantics. A retry with the same key and the same body returns the cached response.
Reuse with a DIFFERENT body is endpoint-specific, because the two
implementations differ. POST /v1/free-rounds hashes the request body
(src/platform_api/free_rounds_flows.py, _request_hash) and answers a
mismatch with 409 idempotency_conflict. POST /v1/sessions uses the
Redis guard (src/infra/idempotency.py), which does not compare
bodies: the cached response is replayed and the new body is not
processed, except for a cached Mobule launch, whose retry body (game,
player and a platform’s sub_operator_ref) is re-read to reauthorize
it: a mismatch answers 403 and storage uncertainty 503 (see
POST /v1/sessions). Never reuse a key across logically different
operations.
A request arriving while the same key is still in flight returns 409 idempotency_in_progress. Over the length limit returns 400 invalid_idempotency_key; missing entirely returns 400 missing_idempotency_key.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Operator’s internal player identifier.
Player’s current real-money balance in minor units (e.g. cents for USD/EUR/GBP). The Aggregator does not hold balances - this is informational only, used for limit checks and game UX.
ISO-4217 alpha-3 fiat code, or a crypto ticker up to 8 chars
(e.g. BTC, ETH, USDT). The authoritative allowlist is the platform
currency registry plus the per-provider/launch gates (see the
supported_currencies on GET /providers/registry), not a static enum -
so this constrains the format only. Crypto requires the launch gate
to be enabled.
ISO 3166-1 alpha-2 country code of the player.
enHTTPS URL to redirect the player after the session ends. When omitted, the Aggregator’s game-complete page is used. The host must be a public domain name or a globally routable IP address; the Aggregator does not resolve or fetch it.
HTTPS entry URL for the operator’s cashier, used by providers when
the player selects an in-game deposit or top-up action. Point this
at a real cashier destination, not a game launch or return route.
If omitted, it falls back to the effective return_url
(including the Aggregator game-complete fallback) for backward
compatibility. TrueLabs and Apparat currently
forward it as a distinct cashier destination; other providers ignore
it. A provider may top-navigate or reload the browser to this URL;
this field does not invoke an operator-specific deposit modal.
Host rules are the same as for return_url.
Platform accounts only. Selects which brand (sub-operator) under
your platform this launch belongs to. Omitting it bills and reports
the launch under your platform’s default brand rather than
failing - so a code path that forgets the parameter still launches,
but its spins are attributed to default instead of the real brand.
Wire it everywhere you launch. Sending it from a non-platform key is
a 400 (E6006); with no default brand provisioned the call is a 409
(E6010), and a platform configured with require_sub_operator_ref
gets a 400 (E6005) instead of the default - opt into that if a
missing parameter should be loud.
The first request bearing an unseen ref - a launch, or a
GET /v1/games - creates the brand with the same games and providers your
platform already has available; afterwards the
same ref always resolves to the same brand. Matched
case-insensitively - Brand-01 and brand-01 are one brand, not
two.
Format: 1-32 characters of [A-Za-z0-9_-], starting and ending
alphanumeric. : is not allowed. A ref that does not match is a 400
(E6004) - refs are validated, never silently repaired, because a
repaired typo would mint a phantom brand and split its money.
Omit it, never send it empty. An absent field resolves to the
default brand; "" (or whitespace) is a 400 (E6004). The two are
different on purpose: a field you never sent cannot have been meant
to name a brand, whereas a field you sent blank means your caller
has the parameter and the value behind it came out empty - that
request believes it selected a brand, and answering it with the
default one would bill a different brand’s ledger and return 201 as
if nothing were wrong.
Public IP address of the player’s device, as your own front end saw it
(IPv4 dotted or IPv6 text; an IPv4-mapped IPv6 address is treated as IPv4).
Up to 45 characters, no zone index (%eth0). Private, loopback, link-local,
carrier-grade-NAT (100.64.0.0/10), documentation, reserved and multicast
addresses are not player addresses. Recommended on every launch; a
provider may require it, and then the launch is refused with E3009/E3010.
Omitted, JSON null, or a non-string value is absence or dropped.
The player’s browser User-Agent header, with surrounding whitespace
trimmed. Up to 512 characters after trimming,
no control characters. Recommended on every launch; a provider may
require it, same codes as client_ip. Omitted, JSON null, or a
non-string value is absence or dropped.
mobile, desktop or tablet, case-insensitive. Never required. When it
is omitted, a provider that needs a mobile/desktop flag gets one derived
from user_agent or a documented fallback. Omitted, JSON null, or a
non-string value is absence or dropped.
Responses
Section titled “Responses”Session created.
object
One-time-use signed URL the player is redirected to.
Headers
Section titled “Headers”Present when the response was served from the idempotency cache.
Validation failed, missing/invalid Idempotency-Key header,
unusable player context (E3009), missing player context required
by the provider (E3010), or unsupported_currency.
Nested Error (error.code matching ^E[0-9A-F][0-9]{3}$) is the
player-context and generic-validation shape. Currency refusals
(unsupported_currency) are a flat {error: string, code: string}
body, not the nested object.
object
object
Correlation ID for support escalation.
object
Missing, invalid, or revoked API key.
object
object
Correlation ID for support escalation.
object
Credit limit exhausted (E4001): the organization’s credit
balance is at or below its overdraft floor (-credit_limit).
Top up credits to resume launches. Only live sessions are gated;
in-flight sessions keep operating.
Note on the code: the error reference lists E4001 as
“insufficient balance” in the player-transaction sense. On this
endpoint it is raised by the organization-level credit gate; the
player’s wallet balance is not involved. Branch on the HTTP
status (402), not on the code alone.
object
object
Correlation ID for support escalation.
object
Operator not configured for this provider, game not allowed for
operator, jurisdiction blocked, player self-excluded, or
provider_currency_unsupported, or PROVIDER_ACCESS_REQUIRED for
absent/revoked Mobule approval or a conflicting replay context.
provider_currency_unsupported is a flat {error: string, code: string}
body, not the nested Error object used for coded E* refusals.
object
object
Correlation ID for support escalation.
object
Game not found.
object
object
Correlation ID for support escalation.
object
Idempotency-Key conflict. Two causes, distinguished by the error
field: idempotency_in_progress when a request with the same key is
still in flight (retry shortly), and idempotency_conflict when the
key was already used with a different request body. Only
POST /v1/free-rounds detects the second case; POST /v1/sessions does
not compare bodies and replays its cached response instead, after
reauthorizing a cached Mobule launch (see POST /v1/sessions).
POST /v1/free-rounds answers with the flat FreeRoundError body;
POST /v1/sessions with a flat {error, message} body without
status, and a Retry-After header. A platform’s brand selector
answers the nested Error object on either endpoint.
On POST /v1/free-rounds for Mobule, whose issuance makes no provider
call, an idempotency_in_progress that persists means the issuance
stopped after creating the grant: its replay never gets a result, and
the grant stays pending_issue, never reaches a launch and is closed
as expired by the expiry sweep (every 30 minutes) after its
end_at. Issue a new grant under a new key once it has closed (see
POST /v1/free-rounds/{grant_id}/cancel).
The flat error body of the free-rounds endpoints’ own refusals (those of POST /v1/free-rounds/{grant_id}/cancel, and the idempotency_* 409s of POST /v1/free-rounds), not the nested Error object a platform’s brand selector answers with: error is the machine-readable code and status repeats the HTTP status.
object
The code, e.g. grant_not_found, not_cancellable, issuance_in_progress, provider_error, provider_unavailable.
issuance_in_progress409The grant’s status (409 only).
Mobule 409 only - the state of each game of the grant.
object
object
object
Correlation ID for support escalation.
object
Headers
Section titled “Headers”Seconds to wait before retrying; sent by POST /v1/sessions only.
Rate limit exceeded; consult Retry-After.
object
object
Correlation ID for support escalation.
object
Headers
Section titled “Headers”Seconds to wait before retry.
Provider returned an error when creating the session.
object
object
Correlation ID for support escalation.
object
Provider or authorization storage temporarily unavailable (PROVIDER_ACCESS_UNAVAILABLE).
object
object
Correlation ID for support escalation.