SDK API reference
Every /sdk/v1 endpoint your game client talks to, rendered from the OpenAPI spec.
These endpoints back the game-client SDKs. They all
take a client_sdk API key; routes that read or mutate a specific
player's state additionally require the per-player secret. See
Authentication for the security model.
For the human-readable overview with auth notes and idempotency
guidance, start at the REST API page. This page
is the machine-derived companion — auto-generated from
openapi.sdk.json
on every build, so it can't drift from the implementation.
Health
Connectivity probes.
/pingCheap auth probe.
Returns 200 if the supplied API key validates. Use as a connectivity smoke test before issuing real SDK calls.
Responses
- 200Authenticated.PingResponse
- 401Missing or invalid API key.ErrorResponse
Events
Start and progress event attempts.
/players/:externalId/eventsList events with active windows for a player.
Returns events whose current window is active. The `leaderboardId` is non-null only for `global` mode; grouped/segmented events allocate per-player on `/start`.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Query parameters
| Field | Type | Description |
|---|---|---|
| status | "active" |
Responses
- 200List of active events.EventsListResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
/players/:externalId/events/:eventKey/startStart an attempt.
Upserts the player on first contact and creates a fresh attempt against the event's active window. Returns 202 with the lobby id when the event uses matchmaking and the lobby is still forming.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| eventKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| playerContext | object | |
| idempotencyKey | string |
Responses
- 201Attempt created.StartAttemptResponse
- 202Lobby still forming. Poll `/sdk/v1/lobbies/{lobbyId}` until status=`active`, then retry start.ErrorResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set does not allow this action.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/events/:eventKey/attempts/:attemptId/progressReport progress on an active attempt.
Updates the attempt's metrics and recomputes the score via the event's score formula. Completing the attempt (hitting the target metric or supplying `mode=set` with the final value) automatically triggers the reward pipeline and emits webhooks.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| eventKeyrequired | string | |
| attemptIdrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| moderequired | "increment" | "set" | |
| metricValue | number | |
| metrics | object | |
| occurredAt | string | |
| idempotencyKey | string |
Responses
- 200Updated attempt.ProgressResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Leaderboards
Read live and finalized leaderboards.
/leaderboards/:leaderboardIdRead a leaderboard.
Returns the top `limit` entries (1–200, default 50). When the window is closed the entries come from the materialized table; while active they come from the Redis sorted set with a short-TTL view cache. Pass `includeSelf=true&externalId=<id>` to also receive the player's own rank/score.
Path parameters
| Field | Type | Description |
|---|---|---|
| leaderboardIdrequired | string |
Query parameters
| Field | Type | Description |
|---|---|---|
| limit | integer | |
| includeSelf | boolean | null | |
| externalId | string |
Responses
- 200Leaderboard view.LeaderboardResponse
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/leaderboards/:leaderboardId/streamSubscribe to live score updates over Server-Sent Events.
Opens a persistent `text/event-stream` connection that emits a JSON event each time a player submits progress to this leaderboard. Event types: - `ready` — sent once when the stream is wired (payload: `{ leaderboardId, finalized }`). - `score_update` — sent on each player progress write (payload: `LeaderboardStreamEvent`). - `closed` — sent once when the leaderboard is already finalized; the server then closes the connection. The server also writes SSE comment heartbeats (`:`-prefixed lines) every 15 seconds to keep intermediaries from reaping the socket; clients should ignore them. Bot-evaluator score writes and initial window-open registrations are not broadcast — only player-driven progress.
Path parameters
| Field | Type | Description |
|---|---|---|
| leaderboardIdrequired | string |
Responses
- 200SSE stream open. Body is `text/event-stream`.
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Grants
Pull, claim, and open reward grants.
/players/:externalId/pending-grantsList a player's unclaimed grants.
Returns up to `limit` (1–200, default 50) grants in `pending` status newest-first. A player who has never started an attempt returns an empty list (not 404).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Query parameters
| Field | Type | Description |
|---|---|---|
| limit | integer |
Responses
- 200List of pending grants.PendingGrantsResponse
- 401Missing or invalid API key.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/grants/:grantId/claimClaim a grant.
Atomic transition from `pending` → `claimed` with `ackedBy=sdk`. Replays against an already-claimed grant return the same row + 200 (idempotent). Grants belonging to a different player return 404 (no cross-tenant enumeration).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| grantIdrequired | string |
Request body
| Field | Type | Description |
|---|---|---|
| idempotencyKey | string |
Responses
- 200Grant after claim.ClaimGrantResponse
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/crates/:grantId/openOpen a crate grant.
Atomic: rolls the crate's reward table, creates a `kind=reward` child grant with the rolled entries, flips the crate to `claimed`. Replays return the previously-rolled contents (idempotent via the crate id).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| grantIdrequired | string |
Responses
- 200Crate + rolled contents grant.OpenCrateResponse
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Lobbies
Poll matchmaking lobbies between start retries.
/lobbies/:lobbyIdPoll a lobby waiting to fill.
Used after `/start` returns 202. Once `status=active`, retry `/start` to get an attempt.
Path parameters
| Field | Type | Description |
|---|---|---|
| lobbyIdrequired | string |
Responses
- 200Lobby state.LobbyResponse
- 401Missing or invalid API key.ErrorResponse
- 404Resource not found.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Players
/players/:externalId/registerMint a per-player secret for zero-trust auth.
Game client calls this ONCE on first launch (or after a data wipe). Returns the player row + plaintext `secret` — surfaced only here, the SDK must persist it locally and present it as `X-Player-Secret` on every subsequent player-scoped call. 409 if the player already has a secret; pass `?force=true` (dev/test API keys only) to rotate.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Query parameters
| Field | Type | Description |
|---|---|---|
| force | boolean | null |
Responses
- 201Player registered (or rotated) — plaintext secret in response body.RegisterPlayerResponse
- 401Missing or invalid API key.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Inventory
/players/:externalId/inventoryList the player's platform-managed inventory.
Returns every item the player currently holds. Requires `X-Player-Secret`. For studio-managed inventory games the list is always empty — the studio backend holds the canonical state in that mode.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Responses
- 200Player inventory rows.InventoryListResponse
- 401Missing or invalid API key.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/inventory/:itemKey/consumeAtomically decrement an item quantity.
Requires `X-Player-Secret`. 409 `conflict` if the player doesn't have enough of the item — the ledger never goes negative. Idempotent on `idempotencyKey`.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| itemKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| quantityrequired | integer | |
| reason | string | |
| idempotencyKey | string |
Responses
- 200New holding after the decrement.ConsumeItemResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/walletList the player's wallet entries.
Returns every economy entry the player has touched, including zero-balance rows (so a wallet that's been emptied still surfaces). Requires `X-Player-Secret`. Filter client-side for live balances.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Responses
- 200Player wallet rows.WalletListResponse
- 401Missing or invalid API key.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
/players/:externalId/wallet/:economyKey/debitAtomically decrement a wallet balance.
Requires `X-Player-Secret`. 409 `conflict` on insufficient balance. Credit is intentionally NOT exposed on the SDK surface — only the server API can mint balance (so a leaked SDK key can't print money).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| economyKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| amountrequired | integer | |
| reason | string | |
| idempotencyKey | string |
Responses
- 200New balance after the decrement.DebitWalletResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 409Conflict (e.g., attempt already finished, lobby still forming).ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse
Catalog
/catalogRead the game's items + currencies catalog.
Single-shot read of every item + currency configured for the calling game. Studios call this once at boot and cache locally — pairs with `events.listForPlayer` (which inlines reward-bundle previews) to render the full game UI without a parallel client-side catalog. Game is derived from the API key.
Responses
- 200Items + currencies for the calling game.CatalogResponse
- 401Missing or invalid API key.ErrorResponse
- 429Rate limited. Either the per-player attempt cap was exhausted for the current window, or the per-minute request budget for this bucket (`sdk_read` / `sdk_write` / `server_api`) was exceeded. Responses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` headers.ErrorResponse