Server API reference
Every /server/v1 endpoint your studio backend calls, rendered from the OpenAPI spec.
These endpoints back the server SDKs. They
require a server_integration API key and must never be called
from a game client — leaked, they can mint currency and grant
items.
For the human-readable overview, start at the
REST API page. This page is the machine-derived
companion — auto-generated from
openapi.server.json
on every build, so it can't drift from the implementation.
Health
Connectivity probes.
/pingServer-side auth probe.
Returns 200 if the supplied API key validates against the `server_integration` permission set. Use as a connectivity smoke test from studio backends before issuing real calls.
Responses
- 200Authenticated.ServerPingResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
Grants
Server-side grant create + ack.
/players/:externalId/grantsCreate a grant (server-side).
Used for IAP fulfillment, season-pass tier unlocks, manual make-goods, and any other studio-backend-driven reward flow. Player rows are upserted on first contact (so an IAP for a brand-new player just works). `idempotencyKey` is mandatory — replays return the same grant row with 200 instead of 201. A unique DB index on `(studio, game, idempotency_key)` makes the dedup race-safe across concurrent retries.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| idempotencyKeyrequired | string | |
| kind | "reward" | "crate" | |
| entriesrequired | GrantedRewardEntry[] | |
| expiresAt | string | |
| sourceKind | "api" | "admin" | |
| sourceRefId | string | |
| metadata | object |
Responses
- 200Idempotency-key replay — an existing grant matched. The body is identical to the original `201` response.ServerGrantResponse
- 201Grant created.ServerGrantResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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/ackAck a grant from the studio backend.
Server-side counterpart of the SDK `/claim`. The grant is marked `claimed` with `ackedBy=server_api`. Use this when your backend applied the grant to the player's inventory *before* the client had a chance to claim. Idempotent — re-acking returns the same row.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| grantIdrequired | string |
Responses
- 200Grant after ack.ServerGrantResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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
Lobbies
Push lobbies from a studio-side matchmaker.
/games/:gameId/events/:eventKey/lobbiesCreate a lobby from your own matchmaker.
For studios running their own matchmaking layer that still want bots and the Kraty leaderboard. The studio supplies the `externalPlayerIds`; the platform creates the lobby + leaderboard + bot fillers, and returns the lobby. Idempotent on `key` via the partial unique `external_lobby_key` index — replays return the existing lobby.
Path parameters
| Field | Type | Description |
|---|---|---|
| gameIdrequired | string | |
| eventKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| keyrequired | string | |
| externalPlayerIdsrequired | string[] | |
| capacity | integer | |
| fillBots | boolean | |
| metadata | object |
Responses
- 201Lobby created (or returned from idempotency cache).ServerLobbyResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 404Resource not found.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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
/games/:gameId/lobbies/:lobbyIdRead a lobby from the studio backend.
Server-side counterpart of the SDK lobby read. Useful for support tooling, fraud-review jobs, or a studio matchmaker confirming a pushed lobby promoted as expected.
Path parameters
| Field | Type | Description |
|---|---|---|
| gameIdrequired | string | |
| lobbyIdrequired | string |
Responses
- 200Lobby state.ServerLobbyResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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
Read player profiles for support / fraud workflows.
/players/:externalIdRead a player's platform profile.
One round trip returns the player row plus recent attempts, grants, and lobby memberships, with aggregate summary counters. Mirrors the portal's Player Lookup view but for server-to-server callers — support tools, fraud-review jobs, Slack bots. **Read-only** — force-complete, manual-grant, and force-claim require a member session through the portal so they leave an actor trail.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Query parameters
| Field | Type | Description |
|---|---|---|
| limit | integer |
Responses
- 200Player profile + recent activity.PlayerProfileResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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/:externalId/banSoft-ban a player (block future SDK writes).
Sets the player's `banned_at` flag. Subsequent player-scoped SDK calls (events.start, events.progress, grants.claim, crates.open, wallet.debit, inventory.consume, players.register) return 403 `player_banned`. Existing scores, lobby memberships, and grants stay intact — this is **soft ban**; studios that want full erasure use `POST /players/:externalId/delete` instead. Idempotent: re-banning refreshes the reason but does not re-fire the webhook.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| reasonrequired | string | Operator-supplied free-form reason. Surfaces on the audit row and the `player.banned` webhook payload. |
Responses
- 200Ban applied (or already in effect — see `applied`).BanPlayerResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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/:externalId/unbanLift a soft-ban.
Clears the player's `banned_at` flag. The player can resume SDK writes immediately. Idempotent: unbanning a non-banned player returns `applied: false`.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Responses
- 200Unban applied (or already in effect — see `applied`).UnbanPlayerResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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/:fromExternalId/merge-into/:toExternalIdMerge one player into another.
Folds the source player's record into the target. Reassigns attempts / grants / item + wallet ledgers; sums balances on key collision; dedupes lobby memberships; rewrites the source row to a `__merged_<uuid>__` placeholder so the original external id is free for re-registration. Idempotent: replaying after the merge returns `status: "no_op_already_merged"`. Returns 404 if either player is missing, 422 if the merge is invalid (same player, target banned/deleted).
Path parameters
| Field | Type | Description |
|---|---|---|
| fromExternalIdrequired | string | |
| toExternalIdrequired | string |
Responses
- 200Merge outcome.MergePlayerResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 404Resource not found.ErrorResponse
- 422Conflict — e.g., grant already claimed, lobby wrong mode.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/inventory/:itemKey/grantPlatform-managed inventory increment (IAP fulfilment).
Atomic increment of a platform-managed item. Used for IAP delivery, season-pass tier rewards, manual make-goods. Idempotent on `idempotencyKey` (typically the IAP receipt id). Replays with a different body return 409 `idempotency_conflict`.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| itemKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| quantityrequired | integer | |
| reason | string | |
| sourceRefId | string | |
| idempotencyKey | string |
Responses
- 200New holding after the increment.ServerAdjustItemResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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/revokePlatform-managed inventory decrement (chargeback / refund).
Atomic decrement. 409 on insufficient quantity — the ledger never goes negative.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| itemKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| quantityrequired | integer | |
| reason | string | |
| sourceRefId | string | |
| idempotencyKey | string |
Responses
- 200New holding after the decrement.ServerAdjustItemResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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/creditMint currency into the player's wallet.
Atomic increment of a platform-managed currency. Used for IAP currency fulfilment, support reissues, prize distribution. Credit is server-API-ONLY by design — only the studio's backend can mint balance. Idempotent on `idempotencyKey` (typically the IAP receipt id).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| economyKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| amountrequired | integer | |
| reason | string | |
| sourceRefId | string | |
| idempotencyKey | string |
Responses
- 200New balance after the credit.ServerAdjustWalletResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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/debitDebit currency from the player's wallet (refund / admin correction).
Atomic decrement. 409 on insufficient balance. The client SDK can also debit; this server-side variant exists for server-authoritative spends (your backend deducts before granting an item or unlocking content).
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string | |
| economyKeyrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| amountrequired | integer | |
| reason | string | |
| sourceRefId | string | |
| idempotencyKey | string |
Responses
- 200New balance after the debit.ServerAdjustWalletResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.ErrorResponse
- 409Conflict — e.g., grant already claimed, lobby wrong mode.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
Migration
/migrate/playersBulk-import players from another platform.
Upsert up to 1,000 players in one call. Each row carries its own `idempotencyKey` (typically the studio's stable player id) so retries are safe. Bad rows are captured per-row in the response's `failures` array — the rest of the batch still applies. Webhooks are NOT emitted by default to prevent flooding the studio's own backend during a 100k-player import.
Request body (required)
| Field | Type | Description |
|---|---|---|
| rowsrequired | MigratePlayerRow[] |
Responses
- 200Counts + per-row failures.MigrateOutcome
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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
/migrate/walletBulk-import wallet balances from another platform.
Credit up to 1,000 wallet rows in one call. Each row carries its own `idempotencyKey`; replays are safe. Players are auto-upserted on first contact so studios can run the wallet import without strictly ordering the players import first. Bad rows go in `failures`; the rest apply.
Request body (required)
| Field | Type | Description |
|---|---|---|
| rowsrequired | MigrateWalletRow[] |
Responses
- 200Counts + per-row failures.MigrateOutcome
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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
/migrate/inventoryBulk-import inventory holdings from another platform.
Grant up to 1,000 inventory rows in one call. Each row carries its own `idempotencyKey`; replays are safe. Players are auto-upserted on first contact. `parameters` lets you carry forward per-instance attributes from the source platform (e.g. a granted potion's roll stats). Bad rows go in `failures`; the rest apply.
Request body (required)
| Field | Type | Description |
|---|---|---|
| rowsrequired | MigrateInventoryRow[] |
Responses
- 200Counts + per-row failures.MigrateOutcome
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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
Gdpr
/players/:externalId/deleteErase a player (GDPR Article 17).
Anonymizes the player row in place and cascades the same erasure across attempts, lobbies, and Redis leaderboard meta. The financial ledger (grants, item ledger, wallet ledger) is retained per audit requirements — its `playerId` FK now points at an anonymized row whose externalPlayerId is a `__deleted_<uuid>__` placeholder. Emits one final `player.deleted` webhook with the original external id so the studio backend can mirror the deletion. Idempotent: replays and requests for never-existed players both return `status: "no_op_*"`.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Request body (required)
| Field | Type | Description |
|---|---|---|
| reason | "gdpr_erasure" | "studio_request" | "test" | Legal basis recorded on the audit row. `gdpr_erasure` for Article 17 requests, `studio_request` for non-regulatory deletions (account closure, fraud), `test` for staging/dev cleanup. |
Responses
- 200Erasure outcome — see `status` for the discriminant.DeletePlayerResponse
- 400Request body failed validation.ErrorResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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/exportExport everything Kraty knows about a player (GDPR Article 15).
Returns a JSON bundle containing the player row, every attempt, every grant, full inventory and wallet snapshots, and lobby memberships. Use this to satisfy data-subject "right of access" requests. Each list is hard-capped at 1,000 rows. Returns 404 if the player is unknown to Kraty.
Path parameters
| Field | Type | Description |
|---|---|---|
| externalIdrequired | string |
Responses
- 200Full player data bundle.ExportPlayerResponse
- 401Missing or invalid API key.ErrorResponse
- 403API key permission set is not `server_integration`.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