Skip to content

Open app

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.

Idempotency-Key
required
string
>= 1 characters <= 256 characters

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.

object
game_id
required
string format: uuid
player_id
required

Operator’s internal player identifier.

string
<= 128 characters
balance
required

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.

integer
currency
required

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.

string
/^[A-Z]{3,8}$/
country
required

ISO 3166-1 alpha-2 country code of the player.

string
/^[A-Z]{2}$/
lang
string
/^[a-z]{2}$/
en
return_url

HTTPS 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.

string format: uri
<= 2048 characters
deposit_url

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.

string format: uri
<= 2048 characters
sub_operator_ref

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.

string
client_ip

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.

user_agent

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.

device_type

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.

Session created.

object
session_id
required
string format: uuid
game_url
required

One-time-use signed URL the player is redirected to.

string format: uri
provider_session_uid
string
provider_code
required
string
X-Idempotent-Replayed
string
Allowed values: true

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
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

Missing, invalid, or revoked API key.

object
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

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
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

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
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

Game not found.

object
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

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).

One of:

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
error
required

The code, e.g. grant_not_found, not_cancellable, issuance_in_progress, provider_error, provider_unavailable.

string
issuance_in_progress
message
string
status
required
integer
409
grant_id
string format: uuid
grant_status

The grant’s status (409 only).

string
games

Mobule 409 only - the state of each game of the grant.

Array<object>
object
game_id
string format: uuid
status
string
error
string
nullable
Retry-After
integer

Seconds to wait before retrying; sent by POST /v1/sessions only.

Rate limit exceeded; consult Retry-After.

object
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any
Retry-After
integer

Seconds to wait before retry.

Provider returned an error when creating the session.

object
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any

Provider or authorization storage temporarily unavailable (PROVIDER_ACCESS_UNAVAILABLE).

object
error
required
object
code
required

Machine-parseable error code. See https://hub.aggregator.gg/error-reference/.

string
/^(E[0-9A-F][0-9]{3}|PROVIDER_ACCESS_REQUIRED|PROVIDER_ACCESS_UNAVAILABLE)$/
E0001
message
required
string
request_id

Correlation ID for support escalation.

string
details
object
key
additional properties
any