> For the complete documentation index, see [llms.txt](https://wiki.playgama.com/playgama/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wiki.playgama.com/playgama/mcp/tools.md).

# Tools reference

Your client discovers the Playgama MCP server's tools automatically once connected, and the authoritative list is what the server answers to `tools/list` — this page is for knowing what to ask for, and what each tool will and will not do.

A few rules hold everywhere:

* Every tool works on **your own games** only — the games of your organization, the ones you see in the Playgama developer console. An id that is not yours, or a deleted game, is refused the same way an unknown one is. The two [referral](#referrals) tools take no game: they work on your organization itself.
* Schemas are **strict**: an argument the tool does not declare is refused by name rather than silently dropped.
* Reads are marked read-only; **every write is annotated as destructive**. Clients decide how to use these hints when requesting confirmation.
* `publish_sandbox`, `send_playgama_catalog_update`, `start_sandbox_traffic`, `confirm_cover_upload` and `confirm_archive_upload` advertise `openWorldHint: true` for their public-content or advertising effects. The other tools advertise `false`, including tools that change console records. This hint is separate from whether a tool reads or writes and does not grant it additional access.
* Business refusals (validation, ownership, a stale checksum) come back as readable results the agent can act on, not as protocol errors.

## Launch path

### get\_launch\_steps

The game's path to players, in order — `archive`, `bridge`, `covers`, `form`, `sandbox`, `share`, `traffic` — judged by the latest archive in the game's form (`archiveId`, `null` when there is none or it could not be read). It decides nothing on its own: each step carries what the tools that own it would answer. For a launch workflow, the agent starts with this read and reads it again after each step. A request only to read data does not authorize the writes in that workflow.

`current` is the first required step still holding the path — `null` when none is.

Each step in `steps`:

| Field          | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | The step                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `state`        | Where the step is — see below                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `reason`       | Why it is not `DONE`, as the tool that owns it says it — e.g. `NOT_FOUND` from the Bridge SDK analysis, a `publish_sandbox` refusal reason or the `get_sandbox_traffic` verdict — or `null`                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `required`     | Whether the step can hold the path. `form` and `share` are not required                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `nextTools`    | The tools that move the step. Every set that offers an archive upload, and the `bridge` step's own, starts with `get_game_checklist`. `traffic` in `TODO` with `source: SHARE` lists `get_sandbox_share` before `start_sandbox_traffic` — the start takes the post links; with `source: REFERRAL` it lists `start_sandbox_traffic` and `get_sandbox_traffic` only — no post is needed. With `reason: STALE_PENDING` the lost run is re-queued by a start with the same links or the same `bonusId`, no new post. Empty when it is `DONE` or `CABINET`, or when `blockedBy` names a step to finish first and the step itself is not `UNKNOWN` |
| `blockedBy`    | The step to finish first, kept only while that step holds the path and has tools to call; otherwise `null`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `askDeveloper` | `true` on `sandbox` and `traffic` — their effect cannot be taken back, so the agent asks the developer before acting                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `emptySlots`   | `covers` only: the slots with no usable cover (`square`, `portrait`, `landscape`); absent when the covers could not be read                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `source`       | `traffic` only: where its players come from. Before a campaign starts — `REFERRAL` when a referral bonus is available for the game, otherwise `SHARE`. While one is pending or live — that campaign's kind: `REFERRAL`, `SHARE`, `DIRECT` (a free run without post links) or `PAID`                                                                                                                                                                                                                                                                                                                                                          |
| `bonusId`      | `traffic` with `source: REFERRAL` only: the referral bonus to pass to `start_sandbox_traffic` — and to pass again, unchanged, after an uncertain answer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `runId`        | `traffic` only, while a campaign is pending or live: the run to follow in `get_sandbox_traffic`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

| `state`       | Holds the path | Meaning                                                                                                                                                                                                                                                                                                                                       |
| ------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TODO`        | Yes            | Not done yet. The form is also `TODO` (`reason` `REJECTED` or `WAITING`) when moderation sent an unchanged form back                                                                                                                                                                                                                          |
| `IN_PROGRESS` | Yes            | Under way — the archive is not unpacked yet, a traffic campaign is pending or live, or a traffic start is refused `RUN_IN_PROGRESS`; poll the tool in `nextTools`                                                                                                                                                                             |
| `DONE`        | No             | Nothing left to do. `traffic` is `DONE` once any traffic campaign of the game has finished — referral, share, direct or paid; a bonus that arrives later does not reopen it                                                                                                                                                                   |
| `FAILED`      | Yes            | The archive could not be used (`reason: UNPACK`) — upload a corrected zip                                                                                                                                                                                                                                                                     |
| `BLOCKED`     | Yes            | Refused — `reason` says why, `blockedBy` names the step to finish first when there is one                                                                                                                                                                                                                                                     |
| `UNKNOWN`     | Yes            | A source did not answer, and nothing else: `reason` is `<SOURCE>_UNAVAILABLE` (`ARCHIVES`, `COVERS`, `SUBMISSION`, `SANDBOX` or `TRAFFIC`) and `nextTools` is `get_launch_steps` — call it again. It is neither done nor not done. A step that would be `DONE` on top of such a step is `UNKNOWN` too; the rest of the path is still answered |
| `PARTIAL`     | Yes            | `covers` has one or two of its three slots filled — `publish_sandbox` is refused until all three are                                                                                                                                                                                                                                          |
| `OUTDATED`    | No             | The live sandbox is behind the latest archive or form (`reason: CHANGED`), or the two cannot be compared (`reason: CHANGE_UNKNOWN`) — publishing again brings it up to date                                                                                                                                                                   |
| `AVAILABLE`   | No             | `share` of a live sandbox — never `DONE`, since the link can always be shared once more                                                                                                                                                                                                                                                       |
| `WAITING`     | No             | `bridge` while the Bridge SDK analysis has not answered (`reason: PENDING`). It may never answer, so the path does not wait for it — but the agent does not publish until `bridge` is `DONE`: the Bridge SDK is required for every game, the sandbox included                                                                                 |
| `CABINET`     | No             | `traffic` refused `ORG_LIMIT` or `PAYMENT_REQUIRED` with no referral bonus to use: the share bonus of the game or of the organization is used, and what is left — paid traffic — is bought in the console. No tools                                                                                                                           |

| Parameter       | Type   | Required | Description                                 |
| --------------- | ------ | -------- | ------------------------------------------- |
| `applicationId` | string | Yes      | Game id, as returned by `list_applications` |

## Games

### list\_applications

Every named game of your organization, newest first — `list`, one `{ id, title, status, updatedAt }` per game, and `total`. Takes paging only: no status filter and no search, so the agent matches on the rows it gets back. The order is fixed and does not change when you edit a game, so paging visits every game exactly once.

Deleted games never appear here, and neither does a game whose title was never filled in — the developer console does not list those either.

| Parameter | Type    | Required | Description                      |
| --------- | ------- | -------- | -------------------------------- |
| `limit`   | integer | No       | 1–100. Default 20                |
| `skip`    | integer | No       | How many rows to skip. Default 0 |

### create\_application

Creates a new game as a draft, the way the console's **New Game** page does, and answers its `id` — the id every other tool takes.

Titles are not unique, so after a call that failed without a clear answer the agent should check `list_applications` before creating again; a duplicate draft is deleted by a human in the console.

| Parameter | Type   | Required | Description                                                                                                                                                                                                                                                                                                                                                                                    |
| --------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`   | string | Yes      | In English — Latin letters, digits, spaces and punctuation, with at least one Latin letter, up to 120 characters. Players and moderators see it as-is                                                                                                                                                                                                                                          |
| `engine`  | enum   | Yes      | `unity`, `construct3`, `cocos-creator`, `defold`, `godot`, `gdevelop`, `game_maker`, `scratch`, `js` (plain JavaScript, no engine)                                                                                                                                                                                                                                                             |
| `agent`   | enum   | No       | The coding agent the game was made with: `claude-code`, `codex`, `cursor`, `antigravity` (Google Antigravity), `opencode`, `gemini-cli`, `kimi-code`, `grok-build`, `workbuddy`, `zcode`, `claude-app` (the Claude app, chat or Desktop), `chatgpt` (ChatGPT chat), `other`, `none` (a game written without an agent). Console field "What agent and model did you use for creating this game" |
| `model`   | enum   | No       | The model behind that agent: `claude-fable`, `claude-opus`, `claude-sonnet`, `claude-haiku`, `openai-astra`, `openai-sol`, `openai-luna`, `openai-terra`, `gemini-pro`, `gemini-flash`, `grok`, `kimi`, `qwen`, `deepseek`, `glm`, `other`                                                                                                                                                     |
| `modelId` | string | No       | The exact model behind that agent, as your runtime names it, e.g. `claude-opus-5-5` or `anthropic/claude-opus-5.5`, up to 100 characters. Stored lowercased, without the vendor prefix or a `:variant` suffix; when the id names a known family, that family is stored as `model`, over the one sent                                                                                           |

The server asks the agent to set `agent` and `model` to itself and the model it runs on, and `modelId` to the exact model id its runtime reports; nobody checks the answer. For a game written without an agent, `agent` is `none` and `model` and `modelId` are left out.

### get\_application

The full saved form of one game, together with the metadata of its archives and media. File contents and download links are not part of this surface. It includes `story`, the game's note to players (see `update_application_form`), which is `null` until one is written. It also includes `modelId`, the exact model the game was made with (see `create_application`), which is `null` until one is saved, and `activeArchives`: the archive each destination serves now, one `{ gameArchiveId, platform, updatedAt }` per destination the game is live on, `PLAYGAMA_SANDBOX` (the sandbox) and `PLAYGAMA` (the catalog) among them.

| Parameter       | Type   | Required | Description                                 |
| --------------- | ------ | -------- | ------------------------------------------- |
| `applicationId` | string | Yes      | Game id, as returned by `list_applications` |

### update\_application\_form

Saves form fields of a game. This is a **partial save**: a field left out keeps its stored value, so the agent sends only what it means to change. Enum arrays are replaced whole. Saving does not submit anything to moderation.

| Parameter              | Type    | Required | Console field                                                                                                                                                                                                                                                                                                              |
| ---------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId`        | string  | Yes      | —                                                                                                                                                                                                                                                                                                                          |
| `title`                | string  | No       | Title — the same rules as in `create_application`                                                                                                                                                                                                                                                                          |
| `description`          | string  | No       | Description — the main idea, the genre and what makes the game unique. A submit needs at least 100 characters; a shorter draft still saves                                                                                                                                                                                 |
| `howToPlayText`        | string  | No       | How to play — the objectives, how to win and the main controls. A submit needs at least 100 characters; a shorter draft still saves                                                                                                                                                                                        |
| `engine`               | enum    | No       | Game Engine — the same values as in `create_application`                                                                                                                                                                                                                                                                   |
| `category`             | enum    | No       | Category — the game's core mechanic: what the player spends most of the time doing, not its theme or setting. One of the 28 values listed below the table. Only a value of the list is accepted, so the tool can change a category but not clear it                                                                        |
| `link`                 | string  | No       | Link to the game if it is published elsewhere — moderation uses it to confirm the game is authentic. An empty string clears it                                                                                                                                                                                             |
| `isHorizontal`         | boolean | No       | Screen Orientation — landscape                                                                                                                                                                                                                                                                                             |
| `isVertical`           | boolean | No       | Screen Orientation — portrait. A submit of a game that supports `IOS` or `ANDROID` needs at least one of the two orientations                                                                                                                                                                                              |
| `leaderboards`         | boolean | No       | Game Features — leaderboards. Only the declaration: the leaderboards themselves are set up with the [leaderboard tools](#leaderboards)                                                                                                                                                                                     |
| `multiplayer`          | boolean | No       | Game Features — multiplayer. A game with this flag cannot be published to the sandbox (`MULTIPLAYER`). Turning it on is refused with 403 `MODERATION_UNAVAILABLE` while `get_submission_state` answers `catalogAvailable: false`; turning it off is always accepted                                                        |
| `social`               | boolean | No       | Game Features — social                                                                                                                                                                                                                                                                                                     |
| `distributeEverywhere` | boolean | No       | Distribution — Playgama may distribute the game on any partner platform where it is not already published                                                                                                                                                                                                                  |
| `excludedPlatforms`    | array   | No       | Distribution — partner platforms the game must not be distributed on. An empty list excludes none                                                                                                                                                                                                                          |
| `supportedLanguages`   | array   | No       | Game Languages. A submit needs at least one                                                                                                                                                                                                                                                                                |
| `supportedDevices`     | array   | No       | Supported Devices — `IOS`, `ANDROID`, `DESKTOP`. A submit needs at least one                                                                                                                                                                                                                                               |
| `agent`                | enum    | No       | What agent and model did you use for creating this game — the agent, the same values as in `create_application`                                                                                                                                                                                                            |
| `model`                | enum    | No       | The same field — the model behind that agent, the same values as in `create_application`                                                                                                                                                                                                                                   |
| `modelId`              | string  | No       | The exact model behind that agent, as your runtime names it, e.g. `claude-opus-5-5` or `anthropic/claude-opus-5.5`, up to 100 characters. Stored lowercased, without the vendor prefix or a `:variant` suffix; when the id names a known family, that family is stored as `model`, over the one sent                       |
| `story`                | string  | No       | Not on the console form — the developer's note to players about the game, 150–900 characters after trimming. In English (every letter in the Latin script) and without links: `https://…`, `www.…` or an e-mail address is refused, a bare domain such as `three.js` is fine. `null` is refused: a story cannot be cleared |

The accepted values of the enum arrays are published to your client in the tool schema, so the agent sees the current list without guessing. For what each field means to a moderator, see [Submitting a game](/playgama/submitting-a-game.md).

`category` takes these values, in priority order: `strategy`, `first-person-shooter`, `matching`, `merge`, `tetris`, `hidden-object`, `ragdoll`, `runner`, `bike`, `management`, `io`, `horror`, `obby`, `shooting`, `idle`, `sports`, `drawing`, `board`, `puzzle`, `simulation`, `driving`, `arcade`, `action`, `adventure`, `words`, `quiz`, `cards`, `beauty-games`. The categories overlap by design, so the names alone are not enough: what each one is, which games are like it and which are not, and the rule for choosing between two come from [`get_game_categories`](#get_game_categories). The field's description in the tool schema only sends the agent there, and the server instructions tell it to read that tool before setting or changing a category and to choose by the core mechanic — not by the title, the theme or the Playgama catalog's categories. The field is optional: a submit does not require it. It is versioned with the rest of the form — picking or changing it counts as a change for the next submit and sandbox publication — and moderators see it with the submitted game. It is the developer's own answer: it does not set the game's category in the Playgama catalog.

`story` is what the game is and how it was made, told to players in the developer's voice. It is optional, and the tool schema tells the agent when and how to write it: before the first sandbox publication or whenever `get_application` shows it empty, in English, the way a developer posts in the game's chat. It is not shown anywhere yet. It is kept beside the form, not in its versions: saving a story alone neither makes the next sandbox publish write a new revision nor gives moderation anything new. A story that breaks its rules is refused with 400, and so is the rest of that save — send the other fields again.

Covers, screenshots and archives are **not** form fields here: covers have their own tools, and other assets are not on this surface at all.

### get\_game\_categories

The values `update_application_form` takes for `category`, in priority order, and how to choose one — the same list the developer console shows in the form. The agent reads it before setting or changing a game's category. It reads no game, so the answer is the same for every organization. It is read-only and comes with the **Edit the game form** permission, beside `update_application_form`.

The guide is a tool's answer rather than part of the form's schema because clients keep only so much of a tool's schema: with the guide inside it, some clients dropped every field description of the form.

| Field         | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `howToChoose` | The rule. Pick by the core mechanic — what the player spends most of the time doing; theme and setting do not decide it, and a game built on fear is `horror` whatever its mechanic. If two categories fit, check their `notLike`; if both still fit, take the one listed first. Decide from the game itself — what a round looks like, its controls and its code — not from the Playgama catalog's categories, which follow another list |
| `categories`  | All 28, in priority order, one `{ value, label, description, like, examples, notLike }` each: `value` is what `update_application_form` takes, `label` the name the console shows, `description` what the category is, `like` the kinds of games it holds and `examples` well-known ones (it can be empty), `notLike` the games it does not hold, each with the category they belong to after `→`                                         |

This tool takes no parameters.

### get\_submission\_state

Whether the game can be submitted to moderation right now and, when it cannot, why — the first rule the submit itself would refuse with. `reason`, in that order: a banned organization (`ORGANIZATION_BANNED`), a game that cannot go to moderation (`MODERATION_UNAVAILABLE`, with `catalogAvailable: false`), missing certification, a cooldown after a failed moderation, the state of the build being judged (`ARCHIVE_PENDING`, `ARCHIVE_FAILED`, the build check's own reason codes, or `ARCHIVE_CHECKING` while it runs), a build with no Playgama Bridge version or Bridge config to build with (`ARCHIVE_NOT_BUILDABLE` — integrate the Bridge SDK and upload it again), an incomplete form (`FORM_INCOMPLETE`, with the fields to fill in `details`, the title included — the submit refuses it), missing covers (`COVERS_INCOMPLETE`, with the empty slots in `emptySlots`), or nothing changed since the last submit (`NO_CHANGES`). `message` is the console's wording of that refusal — the agent relays it to the developer word for word.

With `reviewType: AUTO` the verdict is that of [`send_playgama_catalog_update`](#send_playgama_catalog_update) — an update of a game live in the Playgama catalog without moderation: the same reasons, then `AUTO_REVIEW_UNAVAILABLE` when the game is not live in the catalog (`autoReviewPlatforms` is empty), so only moderation can take it.

The verdict is always about a build: name the archive you are asking about. There is no default.

Whether the game may go to moderation at all is `catalogAvailable`: while it is `false` the answer is always `MODERATION_UNAVAILABLE`, whatever the build, and the agent never tells the developer the game can be submitted. A game never submitted to moderation, or one moderation rejected or postponed, whose sandbox is not blocked (`get_sandbox_state`), opens once its sandbox reaches the targets shown on its sandbox card in the developer console, counted once a day. A game moderation took off, or whose sandbox is blocked, only Playgama can reopen: the sandbox targets do not.

Submitting to moderation is not available through MCP — a human does it in the developer console.

| Parameter       | Type   | Required | Description                                                                                                                                                                           |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                                                                                                                                               |
| `archiveId`     | string | **Yes**  | The build the verdict judges — the archive a submit would freeze. `get_application` and `get_launch_steps` answer the game's archives; an archive of another game is refused with 404 |
| `reviewType`    | string | No       | `MANUAL` (left out): judge a submit to moderation. `AUTO`: judge `send_playgama_catalog_update`                                                                                       |

### list\_moderation\_comments

The moderation correspondence on a game, newest first, grouped by moderation task. Read-only: replying to moderation is not part of this surface.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

## Game builds

Uploading a build is three steps, and the bytes never travel through MCP: the agent gets a presigned URL, performs the upload itself, then confirms.

{% hint style="info" %}
**start → PUT → confirm → poll.** The upload URL is valid for one hour and is signed for exactly the byte length and content type you declared, so the storage refuses anything else. Nothing is in the game's form until the confirm.
{% endhint %}

### start\_archive\_upload

Step 1 of 3. Creates an archive record for the game and answers `uploadUrl` and `headers` — PUT the zip to it with exactly those headers:

```bash
curl -T game.zip -H "Content-Type: application/zip" "<uploadUrl>"
```

The zip must meet the [Game checklist](/playgama/mcp/game-checklist.md): read it with `get_game_checklist` and check the game and the zip against each item before this call.

| Parameter       | Type    | Required | Description                                                                                                                                                                             |
| --------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id                                                                                                                                                                                 |
| `name`          | string  | Yes      | Up to 200 characters. Put the build version in it, e.g. `coin-town-1.4.2` — the form keeps every archive, and the version is how a human tells them apart. A trailing `.zip` is dropped |
| `contentSize`   | integer | Yes      | Exact size of the zip in bytes (`wc -c < game.zip`). At most 300 MB                                                                                                                     |

### confirm\_archive\_upload

Step 3 of 3: tells the console the PUT has finished. The archive is added to the game's form and queued for unpacking and Bridge SDK analysis. Archives already in the form stay there — removing one is a human action in the console.

After unpacking succeeds, the build files are publicly accessible at the archive's game URL used by the QA Tool. Confirmation starts that processing and therefore advertises `openWorldHint: true`; it does not publish or update the game's sandbox page, which requires `publish_sandbox`.

Answers what `get_archive_status` answers for the archive; the agent keeps polling that tool. Refused with 409 while nothing is stored at the upload URL. Safe to repeat: once processing has run, a repeat only answers the current state.

| Parameter       | Type   | Required | Description                           |
| --------------- | ------ | -------- | ------------------------------------- |
| `applicationId` | string | Yes      | Game id                               |
| `archiveId`     | string | Yes      | As answered by `start_archive_upload` |

### get\_archive\_status

The state of one archive. Unpacking and the build check take a minute or more, so the agent polls this until `state.status` is no longer `CHECKING`, or until `processing` is `FAILED` (upload a corrected zip — a broken zip is never checked).

| Field          | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state`        | The build check. `status` — `CHECKING` while it runs, then `PASSED`, `PROBLEM` or `NOT_CHECKED`; `reason` — the code; `message` — what is wrong and what to do, to be given to the developer word for word on `PROBLEM` or `NOT_CHECKED`. Only a `PASSED` build is published to the sandbox or submitted to moderation                                                                                                                                                                                                                                                                               |
| `processing`   | `PENDING` while the upload is unconfirmed or being unpacked; `DONE` once unpacked; `FAILED` if the zip could not be used                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `failedAt`     | `UNPACK` when `processing` is `FAILED`, otherwise `null`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `bridgeSdk`    | Whether that same check found the Playgama Bridge SDK in the build. `FOUND`; `NOT_FOUND` — the SDK was not detected: tell the developer and point them to the certification link from `get_archive_qa_tool_link` (its Build Startup section shows why the SDK did not initialize), and `get_bridge_sdk_docs` has the integration docs. The Bridge SDK is required for every game, the sandbox included: fix the integration and upload a new archive; `PENDING` — the check has not answered yet. Sandbox traffic is what needs `bridgeSdk: FOUND`; publishing and submitting are decided by `state` |
| `errorMessage` | Why it failed, beside `failedAt: UNPACK`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `inForm`       | Whether the archive is in the game's form                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `analysis`     | What the check measured — `bridgeVersion` and `bridgeEngine`, read from the same check `state` comes from; `null` when no check has run                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |
| `archiveId`     | string | Yes      | Archive id  |

## Covers

The form has three cover slots, and each takes an image of exactly that size — resize before uploading:

| Slot        | Size        |
| ----------- | ----------- |
| `square`    | 800 × 800   |
| `portrait`  | 1080 × 1920 |
| `landscape` | 1920 × 1080 |

### start\_cover\_upload

Step 1 of 3, one slot per call. Answers `mediaId`, `uploadUrl` and `headers`:

```bash
curl -T cover.png -H "Content-Type: image/png" "<uploadUrl>"
```

| Parameter       | Type    | Required | Description                                                          |
| --------------- | ------- | -------- | -------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id                                                              |
| `cover`         | enum    | Yes      | `square`, `portrait`, `landscape`                                    |
| `format`        | enum    | Yes      | `png` or `jpeg` — checked against the bytes on confirm               |
| `contentSize`   | integer | Yes      | Exact size of the file in bytes (`wc -c < cover.png`). At most 10 MB |

### confirm\_cover\_upload

Step 3 of 3. The file is checked — it must be the format the upload was started as, exactly the size of its slot, and a complete, decodable image — then published at a permanent public media address, and only then put into the form **in place of** the cover that was there; `replaced` names what it took out.

Because it publishes the cover, this step advertises `openWorldHint: true`.

Refused with 409 while nothing is stored at the upload URL, with 422 when the file does not fit (with the reason — upload a corrected file with a new `start_cover_upload`), and with 502 when the cover could not be published: then nothing is confirmed, and the confirm can be repeated. Safe to repeat: a confirmed cover only answers its state.

| Parameter       | Type   | Required | Description                         |
| --------------- | ------ | -------- | ----------------------------------- |
| `applicationId` | string | Yes      | Game id                             |
| `mediaId`       | string | Yes      | As answered by `start_cover_upload` |

## In-app catalog

{% hint style="warning" %}
`replace_in_app_products` is a **whole-state write**, not a patch: a product left out is deleted, and an empty list clears the catalog. The agent must call `list_in_app_products` first and pass its `checksum` back — if someone edited the catalog in the browser meanwhile, the call is refused instead of deleting their work.
{% endhint %}

### list\_in\_app\_products

The catalog of a game as currently saved, plus the `checksum` naming that exact state. Each product comes back in exactly the fields a replace accepts, so the answer can be sent straight back with one field changed.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### replace\_in\_app\_products

Replaces the whole catalog with the list passed in. The agent asks the developer before it removes a product. The form's "has in-game purchases" flag travels with it: leave `hasInGamePurchases` out and it follows whether the list is empty. Turning purchases on with an empty catalog is refused, and so is a call whose `expectedChecksum` no longer names the saved catalog (409) — read it again, re-apply the change and retry. While purchases are off, a call that leaves them off is refused (409 `IN_GAME_PURCHASES_OFF`): turn them on with `hasInGamePurchases: true` to change the catalog.

| Parameter            | Type    | Required | Description                                                               |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------- |
| `applicationId`      | string  | Yes      | Game id                                                                   |
| `products`           | array   | Yes      | The complete catalog after the change                                     |
| `expectedChecksum`   | string  | Yes      | The `checksum` `list_in_app_products` answered — the state being replaced |
| `hasInGamePurchases` | boolean | No       | Defaults to whether `products` is non-empty                               |

Each product:

| Field         | Type   | Required | Description                                                                                   |
| ------------- | ------ | -------- | --------------------------------------------------------------------------------------------- |
| `productId`   | string | Yes      | Stable id, unique in the catalog                                                              |
| `title`       | string | Yes      | —                                                                                             |
| `description` | string | No       | —                                                                                             |
| `type`        | enum   | Yes      | `consumable` — bought again and again (coins, lives); `non_consumable` — bought once and kept |
| `priceUsd`    | number | Yes      | Price in USD, 0 or more, at most 2 decimals                                                   |

## Leaderboards

Deleting a leaderboard is not available through MCP — it takes its scores with it and cannot be undone, so it stays a human action in the Playgama developer console.

### list\_leaderboards

The leaderboards configured for a game. Each row carries `id` — the tag the game passes to the Playgama Bridge SDK and the one `update_leaderboard` takes — `name`, `type`, `scoreOrder`, `decimals`, `isActive` and the score-protection settings, which are read-only here.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### create\_leaderboard

Adds a leaderboard to a game. `id` is the tag the game passes to the [Playgama Bridge SDK](/playgama/bridge-sdk/api/leaderboards.md): unique within the game, and it cannot be changed later — the agent checks `list_leaderboards` for the tags already taken.

| Parameter       | Type    | Required | Description                                                                                                                                                                                 |
| --------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id                                                                                                                                                                                     |
| `id`            | string  | Yes      | Leaderboard tag, e.g. `top_players`                                                                                                                                                         |
| `name`          | string  | Yes      | Human-readable name, as the console shows it                                                                                                                                                |
| `type`          | enum    | Yes      | What a score is: `numeric` for points, `time` for a duration                                                                                                                                |
| `scoreOrder`    | enum    | No       | `desc` ranks the highest score first, `asc` the lowest — e.g. the fastest time. Default `desc`                                                                                              |
| `decimals`      | integer | No       | Decimal places shown for scores, 0–32767. Default 0. Scores are stored as whole numbers — a fractional score the game submits is rounded — so this does not make fractional scores possible |

### update\_leaderboard

Changes the name, type or score order of an existing leaderboard. At least one of them is required. The tag cannot change.

| Parameter       | Type   | Required | Description                                           |
| --------------- | ------ | -------- | ----------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                               |
| `leaderboardId` | string | Yes      | The leaderboard tag — its `id` in `list_leaderboards` |
| `name`          | string | No       | Human-readable name                                   |
| `type`          | enum   | No       | `numeric` or `time`                                   |
| `scoreOrder`    | enum   | No       | `desc` or `asc`                                       |

## QA Tool

Both tools hand out a link for you to open in a browser. Ask for the link every time rather than building it by hand — the console's address depends on the deployment.

### get\_archive\_qa\_tool\_link

The link that opens an uploaded archive in the Playgama QA Tool, where you play the build inside the Bridge SDK harness and submit it to moderation. The page has something to show once the archive is unpacked (`processing` is `DONE`). With `mode: certification` the link opens the certification page instead — the one to hand out for an archive with `bridgeSdk: NOT_FOUND`: its Build Startup section shows why the Bridge SDK did not initialize, and `get_bridge_sdk_docs` has the integration docs. Opening it unlocks nothing; after the fix, upload a new archive.

Answers `url`, `processing`, `failedAt` and `bridgeSdk`, as `get_archive_status` does.

| Parameter       | Type   | Required | Description                                                                                            |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------ |
| `applicationId` | string | Yes      | Game id                                                                                                |
| `archiveId`     | string | Yes      | Archive id                                                                                             |
| `mode`          | string | No       | `certification` — the only value; anything else is refused. Leave it out for the ordinary testing page |

### get\_local\_game\_qa\_tool\_link

A link that opens the QA Tool on a game you are serving yourself — typically a dev server on `localhost`. Use it to test before uploading anything.

| Parameter   | Type   | Required | Description                                                                                                                                                                    |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `url`       | string | Yes      | Absolute http(s) URL of the game's index page, e.g. `http://localhost:5503/`                                                                                                   |
| `sessionId` | string | No       | Keys the QA Tool's `localStorage`: letters, digits, `-` and `_`, up to 64. Leave it out for a fresh environment every time; pass your own key to keep saved state between runs |

## Bridge SDK documentation

### get\_bridge\_sdk\_docs

The live [Playgama Bridge SDK](/playgama/bridge-sdk/getting-started.md) documentation from this wiki, for integrating the SDK — required in every game before its first upload, the sandbox included — and for fixing it when an archive answered `bridgeSdk: NOT_FOUND` or traffic was refused with `NO_BRIDGE`. It reads the wiki, not your games.

Without `page` it answers the index: `pages`, one `{ page, title, required, description? }` per page of the Bridge SDK section. With `page` it answers that page's `page`, `title`, `url` and `markdown`. With `engine` too, the markdown is cut to that engine's tabs when the page has tabs and one matches; `engineFilter` says which tabs the page had (`tabsFound`) and which were kept (`tabsKept`). An unknown `page` is refused with the list of valid ids.

| Parameter | Type   | Required | Description                                                                                                                                                                                                                                                                                   |
| --------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `page`    | string | No       | Page id from the index, e.g. `api/storage` or `api/advertisement/rewarded`. A link copied from a wiki page works as-is — with or without `.md`, with or without an anchor (the anchor is ignored; the whole page comes back). Up to 200 characters. Leave it out for the index                |
| `engine`  | string | No       | Engine or binding to keep code samples for, matched against the page's tab titles — e.g. `Plain JS`, `Unity`, `Construct 3`, `GDevelop`, `Godot` (matches both 3.x and 4.x), `GameMaker`, `Defold`, `Cocos Creator`, `Scratch`. Up to 200 characters. Leave it out to keep every engine's tab |

### get\_game\_checklist

The live [Game checklist](/playgama/mcp/game-checklist.md) from this wiki: what a game and its archive must meet to go up on Playgama — the required Bridge steps in order, the build requirements and tips. The agent reads it before integrating the Bridge SDK and before every `start_archive_upload`, checks the game and the zip against each item, fixes what fails before the upload, and tells you which items it could not check itself. It reads the wiki, not your games.

Without `page` it answers the checklist itself: `title`, `url`, `markdown`, and `pages` — the pages nested under the checklist, one `{ page, title, required, description? }` each (none yet). With `page` it answers that page's `page`, `title`, `url` and `markdown`. A link to a Bridge SDK page is not this tool's — pass it to `get_bridge_sdk_docs` as its `page`, as written; here it is refused with the list of valid ids.

| Parameter | Type   | Required | Description                                                                                                                                                                                                       |
| --------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `page`    | string | No       | Id of a nested page from `pages`. A link copied from the wiki works as-is — with or without `.md`, with or without an anchor (the anchor is ignored). Up to 200 characters. Leave it out for the checklist itself |

## Game telemetry

The telemetry tools are read-only and work on preaggregated data for one of your organization's games. They do not expose raw events. Analytics responses are dynamic JSON passed through from the analytics backend: fields may be added or omitted, nested objects and arrays remain JSON values, and a missing or `null` value never means zero. Inspect the returned schema version, period, freshness, coverage, sample sizes, warnings and pagination before drawing a conclusion.

Use them as a drill-down: overview first, then only the event, time range or filtered sessions needed for the current question. Do not fetch every page or combine the views into a raw export.

### get\_game\_telemetry\_guide

Agent-only instructions for instrumenting a Playgama game and interpreting its telemetry. The answer is `{ title, version, markdown }`. It covers the current performance-sample contract, bounded product-progression events, privacy and cardinality rules and the analytics workflow. The agent reads it before finishing a game. Telemetry is guidance rather than a publication gate.

This tool has no parameters. Its instruction text belongs to the server response and is not duplicated in this public reference.

### get\_game\_telemetry\_analytics

A compact overview of telemetry volume, the event inventory, sessions, users, duration and freshness for an inclusive date range of at most 31 days. Start here. The event inventory is paginated separately inside the response: pass `events_page.next_cursor` back as `eventCursor` only when more event types are needed for the current question.

| Parameter       | Type    | Required | Description                                                                                |
| --------------- | ------- | -------- | ------------------------------------------------------------------------------------------ |
| `applicationId` | string  | Yes      | Game id, as returned by `list_applications`                                                |
| `dateFrom`      | string  | Yes      | First calendar date, inclusive, in `YYYY-MM-DD` format                                     |
| `dateTo`        | string  | Yes      | Last calendar date, inclusive, in `YYYY-MM-DD` format; the range may span at most 31 days  |
| `eventLimit`    | integer | No       | Event types in this overview page, 1–100. Default 50                                       |
| `eventCursor`   | string  | No       | Opaque `events_page.next_cursor` from the preceding overview call; omit for the first page |

The response includes the applied `events_page.limit` and its next cursor. It is evidence rather than a server-generated conclusion.

### get\_game\_telemetry\_event\_analytics

Preaggregated detail for one event from the overview. Without `fieldName`, it discovers every top-level field with its type, coverage, cardinality and numeric or boolean summary. With `fieldName`, it returns a bounded distribution of the field's top categorical JSON values plus `other_event_count`. A nested object or array is one value of that top-level field. This is the view for FPS, frame-time and stutter summaries from `performance_sample` over the requested period.

| Parameter       | Type   | Required | Description                                                                          |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
| `applicationId` | string | Yes      | Game id, as returned by `list_applications`                                          |
| `dateFrom`      | string | Yes      | First calendar date, inclusive, in `YYYY-MM-DD` format                               |
| `dateTo`        | string | Yes      | Last calendar date, inclusive, in `YYYY-MM-DD` format; at most 31 days               |
| `eventKind`     | enum   | Yes      | Event namespace: `standard`, `custom` or `system`                                    |
| `eventName`     | string | Yes      | Stable event name discovered in `get_game_telemetry_analytics`, up to 256 characters |
| `fieldName`     | string | No       | One top-level payload field to inspect, up to 256 characters                         |

### get\_game\_telemetry\_timeseries

Paginated preaggregated counts in 30-minute or daily buckets for event-volume and sampling-reach comparisons. Optionally narrow the series by event namespace and event name. Points do not contain FPS or other custom-field value distributions. Compare performance values with separate bounded event-analytics requests for compatible date ranges. Follow `page.next_cursor` only while more points are needed for the current conclusion.

| Parameter       | Type    | Required | Description                                                                |
| --------------- | ------- | -------- | -------------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id, as returned by `list_applications`                                |
| `dateFrom`      | string  | Yes      | First calendar date, inclusive, in `YYYY-MM-DD` format                     |
| `dateTo`        | string  | Yes      | Last calendar date, inclusive, in `YYYY-MM-DD` format; at most 31 days     |
| `granularity`   | enum    | No       | `30m` or `1d`. Default `1d`                                                |
| `eventKind`     | enum    | No       | `standard`, `custom` or `system`                                           |
| `eventName`     | string  | No       | Stable event name, up to 256 characters                                    |
| `limit`         | integer | No       | Time-series points in this page, 1–1000. Default 500                       |
| `cursor`        | string  | No       | Opaque `page.next_cursor` from the preceding call; omit for the first page |

The response returns the applied `page.limit` together with `page.next_cursor`.

### get\_game\_telemetry\_session\_facts

Paginated, preaggregated and pseudonymized session facts for product-event sequence questions such as whether a player found an item before dying or restarted after defeat. Supply at least one of `eventName`, `fieldName`, `sessionRef` or `userRef`. Facts contain sanitized dynamic JSON in ordered chunks; they are not a complete user history. Repetitive `core_ping` and `performance_sample` events are intentionally excluded: do not filter this view by either event or use it for per-session FPS analysis.

| Parameter       | Type    | Required    | Description                                                                            |
| --------------- | ------- | ----------- | -------------------------------------------------------------------------------------- |
| `applicationId` | string  | Yes         | Game id, as returned by `list_applications`                                            |
| `dateFrom`      | string  | Yes         | First calendar date, inclusive, in `YYYY-MM-DD` format                                 |
| `dateTo`        | string  | Yes         | Last calendar date, inclusive, in `YYYY-MM-DD` format; at most 31 days                 |
| `eventName`     | string  | Conditional | Event to require in a session, up to 256 characters                                    |
| `fieldName`     | string  | Conditional | Top-level field to require in a session, up to 256 characters                          |
| `sessionRef`    | string  | Conditional | Opaque application-scoped session reference returned by this tool, up to 64 characters |
| `userRef`       | string  | Conditional | Opaque application-scoped user reference returned by this tool, up to 64 characters    |
| `limit`         | integer | No          | Sessions in this page, 1–100. Default 20                                               |
| `cursor`        | string  | No          | Opaque `page.next_cursor` from the preceding call; omit for the first page             |

At least one conditional filter is required. Start with the default page and request another only when it can change the answer. The response returns the applied `page.limit` together with `page.next_cursor`.

## Sandbox

A **sandbox** is a public page where anyone with the link plays the build you uploaded — no submit, no certification, no moderation, no partner platform. One sandbox per game, and its address never changes, so re-publishing keeps the players' saved progress.

{% hint style="danger" %}
`publish_sandbox` puts the game in front of anyone with the link **immediately**, and `start_sandbox_traffic` starts an ad campaign that sends players there and **cannot be undone**. Each has a line of its own on the consent page you see when you connect an agent. Cover confirmation publishes the image at a public media address, and archive confirmation starts processing that makes the unpacked build publicly accessible for QA, as described under `confirm_cover_upload` and `confirm_archive_upload`. Tell your agent to ask before publishing content or starting a campaign.
{% endhint %}

A game is in its sandbox or in the Playgama catalog, never both: once a build goes live in the catalog, the sandbox is taken down and stays closed to the game — its updates go through [`send_playgama_catalog_update`](#send_playgama_catalog_update) or moderation.

Publishing is refused when the game is live in the Playgama catalog, when its form says multiplayer, when the sandbox was blocked, after **15 publications in a rolling hour**, for a game's first publication when the organization has opened as many new sandboxes as its quota allows, when the archive failed to unpack, while it is unpacking, when its build check has not passed or has not answered yet, when the build runs a Playgama Bridge older than **2.2.0** or its version could not be read, when its form is incomplete, the title included, and when not all three covers are in place. `get_sandbox_state` gives the same verdict in advance.

### get\_sandbox\_state

What is on the game's sandbox right now, in `site`: `siteId`, the link (`url`), `status`, `discoveryStatus`, `publishedAt` and the live `revision` — its `id`, `archiveId`, the `title` it was published under, and when (`createdAt`). `site` is `null` when the game has never been published to a sandbox — unless Playgama blocked it: a blocked sandbox is shown with `status: BLOCKED` even then.

| `status`      | Meaning                                                                                                                                                                                                           |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ACTIVE`      | The page serves the live revision                                                                                                                                                                                 |
| `UNPUBLISHED` | Taken down — by Playgama on the developer's request, or because the game went live in the Playgama catalog. The address and `revision` stay; for a game not in the catalog, publishing again brings the page back |
| `BLOCKED`     | Blocked by Playgama. Publishing does not lift it — contact support                                                                                                                                                |

`discoveryStatus` — `VISIBLE` or `HIDDEN` — is read-only here: whether the game may appear in Playgama's public discovery lists, such as the ranking of the most played sandbox games. A hidden game is still playable at its link.

Pass `archiveId` and the answer also carries a `publish` verdict for that archive:

| Field                       | Meaning                                                                                                                                                                                                                                                                                                                                             |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowed`                   | Whether `publish_sandbox` would go through                                                                                                                                                                                                                                                                                                          |
| `reason`                    | When it would not: one of the [`publish_sandbox` refusals](#publish_sandbox) — `PUBLISHED_ON_PLAYGAMA`, `MULTIPLAYER`, `BLOCKED`, `RATE_LIMITED`, `ORG_QUOTA`, `ARCHIVE_FAILED`, `ARCHIVE_PENDING`, the build check's own codes and `ARCHIVE_CHECKING`, `BRIDGE_VERSION_UNKNOWN`, `BRIDGE_VERSION_OUTDATED`, `FORM_INCOMPLETE`, `COVERS_INCOMPLETE` |
| `message`                   | The words of that refusal, the same ones the publish answers; `null` when it is allowed                                                                                                                                                                                                                                                             |
| `changeStatus`              | `CHANGED` — this archive or the form differs from the live revision; `UNCHANGED` — they match, and a publish still builds and publishes them again as a new revision; `UNKNOWN` — cannot be told. None of them refuses                                                                                                                              |
| `archiveChanged`            | Whether that archive differs from the one on the page                                                                                                                                                                                                                                                                                               |
| `failedAt`                  | With `ARCHIVE_FAILED`: `UNPACK`                                                                                                                                                                                                                                                                                                                     |
| `details`                   | With `FORM_INCOMPLETE`: the form fields to fill, the title included — `{ path, property, messages }` each                                                                                                                                                                                                                                           |
| `emptySlots`                | With `COVERS_INCOMPLETE`: the cover slots with no cover (`square`, `portrait`, `landscape`)                                                                                                                                                                                                                                                         |
| `limit`, `used`, `resetsAt` | With `ORG_QUOTA`: the cap that refuses, how much of it is used, and when a slot frees up — `null` for the lifetime cap                                                                                                                                                                                                                              |

A publish needs all three covers and a complete form: without them it is refused with `COVERS_INCOMPLETE` (the empty slots in `emptySlots`) or `FORM_INCOMPLETE` (the fields in `details`). The preview of a shared link is cached by whoever opens it first, and nobody can refresh it afterwards, so the artwork has to be there before the link goes out.

What is live is always `site.revision`. How a publish request went is [`get_publications`](#get_publications), not this read.

| Parameter       | Type   | Required | Description                                                                      |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                                          |
| `archiveId`     | string | No       | An archive of this game. When given, the answer also carries the publish verdict |

### get\_sandbox\_share

The ready post and share links for the game's **live** sandbox: `text`, the post (the live title and the link), and `links` — one per network, `x`, `threads`, `facebook`, `linkedin`, `reddit` — each with its `url` and `prefillsText`. Facebook takes only the link, so the developer pastes the text. `share` is `null` when the sandbox is not published or is blocked.

For the [share bonus](#sandbox-traffic), a post on any platform counts; these links are shortcuts.

Hand out the links as given — never build them by hand.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### publish\_sandbox

Advertises `openWorldHint: true`: this makes the game available to anyone with its public sandbox link.

Publishes an archive to the game's sandbox page. The game must not be live in the Playgama catalog, the archive must be in the game's form and unpacked (`get_archive_status` says `processing: DONE`), and the game must have all three covers, a complete form, the title included, and its multiplayer flag off — the sandbox has no player accounts. The archive must also have passed its build check (`state.status: PASSED` in `get_archive_status`): a `PROBLEM`, a `NOT_CHECKED` or a check still running is refused, and the refusal carries the check's own reason code and its message, which the agent relays word for word. The build must also run **Playgama Bridge 2.2.0 or newer**: an older one is refused with `BRIDGE_VERSION_OUTDATED`, and one whose version the check could not read with `BRIDGE_VERSION_UNKNOWN`. `get_sandbox_state` with the same `archiveId` answers those refusals before this call. The Playgama Bridge SDK is required for every game, the sandbox included, and a build it was not detected in does not pass the check: hand the developer the `get_archive_qa_tool_link` certification link, fix the integration with the docs from `get_bridge_sdk_docs` and upload a new archive.

Answers `outcome: PENDING`: the archive is built, then published, as a rule within minutes. Follow the `PLAYGAMA_SANDBOX` publication of [`get_publications`](#get_publications), waiting its `retryAfterSeconds` between reads: `PUBLISHED` — the build is live; `FAILED` — it did not go live and no reason is given: tell the developer, a new publish starts over. Until it is `PUBLISHED` or `FAILED`, another publish is refused with 409: wait and call again. Every publish writes a new revision, a repeat of the live archive and form included, and counts toward the hourly cap. A live build that no longer passes the checks stays on the page, but is refused. After a publish whose answer never arrived, read `get_publications` before publishing again.

The answer also carries `url`, `status` and `revision` — what is live until the new build lands, `null` before the first publication. Take the link from `url` and hand it to the developer once the publication is `PUBLISHED` — never build it by hand.

After a publish, the agent is told to call `get_sandbox_share` and offer the developer the post and links.

| Parameter       | Type   | Required | Description                                                                                                                                                                                                                                                                             |
| --------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                                                                                                                                                                                                                                                 |
| `archiveId`     | string | Yes      | An archive of this game, in its form, unpacked and past its build check (`processing: DONE` and `state.status: PASSED`), built on Playgama Bridge 2.2.0 or newer (`analysis.bridgeVersion`)                                                                                             |
| `updateNote`    | string | No       | What a returning player will notice in this build, 80–500 characters after trimming, under the same rules as `story` (English, no links). Sent when this archive replaces the live one (`archiveChanged` in `get_sandbox_state`); kept with this publication and not shown anywhere yet |

An `updateNote` that breaks its rules is refused with 400 before anything else is checked, and nothing is published.

A refusal carries `reason` and `message`, and the details named below. Refusals are checked in the order of the table, so a later one never hides an earlier one:

| `reason`                          | Status | What to do                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PUBLISHED_ON_PLAYGAMA`           | 409    | The game is live in the Playgama catalog, and its sandbox is closed to it for good — updates go through [`send_playgama_catalog_update`](#send_playgama_catalog_update) or moderation                                                                                                                                                                 |
| `MULTIPLAYER`                     | 422    | The form's multiplayer flag is set, and the sandbox has no player accounts. A multiplayer game goes through moderation when the game is open to it (`catalogAvailable` in `get_submission_state`); turn the flag off only if the game really plays single-player                                                                                      |
| `BLOCKED`                         | 403    | The sandbox was blocked. Publishing does not lift it — contact support                                                                                                                                                                                                                                                                                |
| `RATE_LIMITED`                    | 429    | 15 publications of this game in the rolling hour already, repeats of the live build included; only a publication that failed is not counted. Wait                                                                                                                                                                                                     |
| `ORG_QUOTA`                       | 429    | A game's first publication only: the organization has opened as many new sandboxes as it may per day, per week or ever. `limit`, `used` and `resetsAt` say which cap and when a slot frees up (`null` for the lifetime cap) — wait until then, do not retry. A new build of a published game, or publishing again after a take-down, is never counted |
| `ARCHIVE_FAILED`                  | 409    | `failedAt: UNPACK` — the zip could not be used, upload a corrected one                                                                                                                                                                                                                                                                                |
| `ARCHIVE_PENDING`                 | 409    | The archive is still being unpacked — poll `get_archive_status` and try again. If `get_archive_status` already says `DONE`, this archive cannot be served — upload it again                                                                                                                                                                           |
| the build check's own reason code | 409    | The check reports a problem with the build, or could not check it. The refusal carries the check's `message`: relay it word for word, fix the build and upload a new archive                                                                                                                                                                          |
| `ARCHIVE_CHECKING`                | 409    | The build check has not answered yet — poll `get_archive_status` until `state.status` leaves `CHECKING`                                                                                                                                                                                                                                               |
| `BRIDGE_VERSION_UNKNOWN`          | 409    | The check could not read the build's Bridge version. Make sure the game uses Playgama Bridge 2.2.0 or newer and upload the archive again                                                                                                                                                                                                              |
| `BRIDGE_VERSION_OUTDATED`         | 409    | The build runs a Playgama Bridge older than 2.2.0; the message names the version found. Update the SDK (`get_bridge_sdk_docs`) and upload a new archive                                                                                                                                                                                               |
| `FORM_INCOMPLETE`                 | 422    | The form is missing required fields, the title included; the refusal names them in `details` — `{ path, property, messages }` each. Fill them with `update_application_form` and publish again                                                                                                                                                        |
| `COVERS_INCOMPLETE`               | 422    | Not all three covers are in place; the refusal names the empty slots in `emptySlots`. Upload them with `start_cover_upload` / `confirm_cover_upload` and publish again                                                                                                                                                                                |

A publish that passes all of them while the game's publication is still `IN_PROGRESS` is refused with 409, without a `reason`: wait for that one in `get_publications`, then publish again.

### get\_publications

The game's latest publication on each destination, newest first: `PLAYGAMA_SANDBOX` — the sandbox, published by `publish_sandbox` — and `PLAYGAMA` — the Playgama catalog, published by `send_playgama_catalog_update` or from the console. A destination is absent while it has no publication on record — before its first, and also when it was last published before these records were kept — so an absent one does not mean never published: what each destination serves is `activeArchives` in `get_application`.

| Field               | Meaning                                                                                                                                       |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                | The publication                                                                                                                               |
| `platform`          | `PLAYGAMA_SANDBOX` or `PLAYGAMA`                                                                                                              |
| `releaseId`         | The release it publishes                                                                                                                      |
| `archive`           | The archive it publishes; `null` only once that archive is gone                                                                               |
| `status`            | `IN_PROGRESS` while its archive is built and published; `PUBLISHED` once its build went live; `FAILED` when it never did — no reason is given |
| `startedAt`         | When it was requested                                                                                                                         |
| `updatedAt`         | When its status last changed                                                                                                                  |
| `retryAfterSeconds` | While `IN_PROGRESS`, how long to wait before reading again; `null` otherwise                                                                  |

A `PUBLISHED` publication may since have been replaced or taken down: what the sandbox serves now is always `site.revision` in `get_sandbox_state`.

This is also the check-up read after a `publish_sandbox` whose answer never arrived. Such a request may still land after any read, so the agent never tells the developer it did not. Compare the `PLAYGAMA_SANDBOX` publication with the one read before the call: a new `releaseId` naming this archive is a publication of this archive — that request's or another's — follow it; otherwise the outcome is unknown, and the agent says so. Another publish is a new request, made on the developer's new yes. After a `send_playgama_catalog_update` whose answer never arrived, see [its section](#send_playgama_catalog_update).

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

## Sandbox traffic

A published sandbox is playable by anyone with the link. `get_sandbox_traffic` reads eligibility, the bonuses on offer and existing campaign state; `start_sandbox_traffic` starts an advertising campaign in the Playgama DSP, with banners cut from the game's covers, pointed at the sandbox page. The console's game page has the same start button in its Sandbox block. Paid traffic is bought in the console; MCP does not purchase it.

The free traffic an agent can start comes from two independent bonuses, and one start takes exactly one of them:

* **The share bonus.** The developer posts about the game on any platform and pastes the links of the published posts; `start_sandbox_traffic` takes one to three of them in `postUrls`. Each accepted link adds one boost unit to the same campaign, up to three. The links can come at once or later, and each addition extends the campaign to at least three days from that moment, even after it has finished. `get_sandbox_share` has the ready post and share links. The bonus is once per game, for up to three games per organization over its lifetime; earlier free runs without sharing do not use it.
* **A referral bonus** — traffic earned through [invitations](#referrals). It needs no post: `start_sandbox_traffic` takes its `bonusId`. Referral bonuses do not count against the share bonus limits.

The agent reads how long a boost serves and how many share bonuses are left (`durationDays`, `remainingFreeRuns`) from the `offer` of `get_sandbox_traffic`, and describes a share boost without a dollar amount — `budgetUsd` is internal accounting.

Before a start can go through, the game needs a published sandbox, covers in the live revision, and a live build in which the Bridge SDK was found. All campaigns share one limit on how many run at once; earlier free or paid campaigns can run alongside a bonus.

### get\_sandbox\_traffic

Reads whether traffic can be brought to the game's sandbox right now, the bonuses on offer, and the state of existing runs. It does not start a campaign or spend a bonus.

| Field             | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `verdict`         | The share bonus: `allowed` — whether the game is eligible now — and `reason` when it is false: the very refusal `start_sandbox_traffic` with post links would give. Post links are not part of it; the start checks them                                                                                                                                                                                                                        |
| `offer`           | The share bonus: `kind` `FREE` while the game's share bonus is unused, `PAID` after; `available` — true only for a free bonus that can start now; `remainingFreeRuns` — share bonuses left to the organization, lifetime; `durationDays` — how long one boost serves; `budgetUsd` — per accepted post, internal accounting, not a figure to quote; `priceUsd` (`null` for the free bonus) and `expectedGameplays`                               |
| `referralOffer`   | A referral bonus for this game, independent of `verdict` and `offer`: `available`; `reason` when it is not — one of the eligibility reasons below (`DSP_DISABLED` to `NO_BRIDGE`), `RUN_IN_PROGRESS`, or `NO_REFERRAL_BONUS` when the organization has none to use; `bonusId` — the bonus to start, one already reserved for this game first; `availableBonuses`; `budgetUsd` and `durationDays` of that bonus; `runId` — its run, once started |
| `hasCompletedRun` | Whether any traffic campaign of the game has finished, over its whole history                                                                                                                                                                                                                                                                                                                                                                   |
| `current`         | The run in progress — `PENDING` while the campaign is being created or updated, `RUNNING` while it serves — or `null`. It can be another campaign than the one the agent started                                                                                                                                                                                                                                                                |
| `runs`            | The history, newest first                                                                                                                                                                                                                                                                                                                                                                                                                       |

Each run carries `id`, `kind` (`FREE`, `REFERRAL` or `PAID`), `status` (`PENDING`, `RUNNING`, `FINISHED`, `FAILED`), `failureReason` when it failed, `postUrls` — the post links accepted for it, empty for referral and paid runs and for earlier free runs without sharing — `creativeCount`, `budgetUsd`, `createdAt`, `startsAt` and `endsAt`, and `spentRatio` — the share of the budget the DSP reports spent, 0 to 1, `null` until the DSP was first asked. `spentFinal` becomes true once the figure is no longer expected to change.

The `reason` values, with the status `start_sandbox_traffic` answers each with:

| `reason`            | Status | What it means                                                                                                                                                                                                                                                        |
| ------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DSP_DISABLED`      | 503    | Traffic is not available on this deployment                                                                                                                                                                                                                          |
| `NO_SITE`           | 422    | The game was never published to its sandbox — call `publish_sandbox` first                                                                                                                                                                                           |
| `BLOCKED`           | 403    | The sandbox is blocked                                                                                                                                                                                                                                               |
| `NO_COVERS`         | 422    | The live revision has no covers — upload covers and publish again, the banners are cut from them                                                                                                                                                                     |
| `ANALYSIS_PENDING`  | 422    | The analysis of the live build has not answered yet — ask again shortly                                                                                                                                                                                              |
| `NO_BRIDGE`         | 422    | The Playgama Bridge SDK was not detected by the analysis of the live build — traffic goes only to games the ad network can serve; `get_bridge_sdk_docs` has the integration docs. A build the SDK was detected in is not refused, whatever the analysis run ended as |
| `RUN_IN_PROGRESS`   | 409    | A share run is pending or live, a paid launch is pending, the referral bonus's own run is pending, or the limit on campaigns running at once is reached                                                                                                              |
| `PAYMENT_REQUIRED`  | 402    | The game's share bonus is used — paid traffic is offered in the console. A referral bonus can still be used                                                                                                                                                          |
| `ORG_LIMIT`         | 429    | The organization has used its three share bonuses, lifetime; earlier free runs without sharing do not count                                                                                                                                                          |
| `NO_REFERRAL_BONUS` | 422    | `referralOffer` only: there is no referral bonus to use for this game                                                                                                                                                                                                |

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### start\_sandbox\_traffic

Starts a free campaign, or adds post links to the game's share campaign. **It spends the bonus's budget over its window and cannot be undone** — ask the developer before calling it.

Advertises `openWorldHint: true` because it starts an advertising campaign that brings players to the public game.

It takes exactly one source:

* `bonusId` — a referral bonus from `get_referral_program` or from `referralOffer` in `get_sandbox_traffic`. No post is needed. After an uncertain answer — a timeout, a lost response — the agent repeats the call with the **same** `bonusId` and `applicationId`, which answers the same run; it never picks another bonus to retry. A bonus started for one game stays with that game. Only a confirmed failure before the campaign reached the DSP makes the bonus usable again, and starting it again restarts the same run; while the DSP's answer is unknown, the bonus stays reserved.
* `postUrls` — links to posts the developer has already published: the agent asks the developer to paste them and never invents one. Each accepted link adds one boost unit to the game's share campaign, up to three per game. Sending links that were already accepted changes nothing — the same run comes back with the same budget and dates. New links can be added later, up to three in total; each addition extends the campaign to at least three days from that moment, even after it has finished. New links wait while a launch or an update is `PENDING` (`RUN_IN_PROGRESS`).

Answers `run`, the `PENDING` run with its `id` and the stored `postUrls`; the campaign is created or updated in the background. Poll `get_sandbox_traffic`, find that `run.id` in `runs` and follow it until it leaves `PENDING` — `RUNNING`, or `FAILED` with `failureReason`; `current` may name another campaign. A timeout is not permission to spend another bonus. A failed share run gives the bonus back, so starting again after it is safe. A `PENDING` run whose job was lost is re-queued by a start with the same links or the same `bonusId` — the same run, no second bonus.

| Parameter       | Type             | Required | Description                                                                                                                                                                                                                                                                                                             |
| --------------- | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string           | Yes      | Game id                                                                                                                                                                                                                                                                                                                 |
| `bonusId`       | string           | One of   | A referral bonus id, up to 128 characters, from `get_referral_program` or `get_sandbox_traffic`. Never together with `postUrls`                                                                                                                                                                                         |
| `postUrls`      | array of strings | One of   | 1–3 different HTTPS links to public posts on any platform, at most 2048 characters each. Links are normalized and only their syntax is checked — explicit HTTPS, a valid host, no credentials or custom port — and duplicates are refused. Domains and paths are not restricted, and what the posts say is not verified |

A refusal carries `reason`: one the `get_sandbox_traffic` table above shows in advance, or one of the source refusals below, which the read cannot show. An array outside 1–3 items, a link over 2048 characters or a `bonusId` over 128 characters is refused by the input schema, and the exact same link twice by input validation, before these checks.

| `reason`                 | Status | What to do                                                                                                       |
| ------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `SHARE_REQUIRED`         | 422    | Neither a bonus nor post links — share the game and pass 1–3 public post links, or pass a referral `bonusId`     |
| `INVALID_POST_URL`       | 422    | A link is not a valid HTTPS URL (no credentials or custom port), or the same link is given twice — fix the links |
| `INVALID_TRAFFIC_SOURCE` | 422    | Both `bonusId` and `postUrls`, or a blank `bonusId` — send exactly one source                                    |
| `NO_REFERRAL_BONUS`      | 422    | The bonus is unknown, belongs to another organization, or is already spent                                       |
| `BONUS_GAME_MISMATCH`    | 409    | The bonus was already started for another game, and it stays with that game                                      |

## Playgama catalog

A game live in the Playgama catalog takes an update without moderation. Its first publication, and every other platform, go through moderation, which the developer submits in the console.

### send\_playgama\_catalog\_update

Advertises `openWorldHint: true`: every player of the game in the Playgama catalog gets this build, and no moderator checks it first. It has a consent line of its own, `playgama-catalog-update`. The agent calls it only on the developer's explicit yes to this update, after naming the game, the build and that the game live in the catalog is updated for all its players without moderation; a yes to an earlier update or to a sandbox publish does not count.

Sends the archive and the saved form to the Playgama catalog — the console's submit without moderation, for the catalog only. Before the call, [`get_submission_state`](#get_submission_state) with the same `archiveId` and `reviewType: AUTO` answers whether it would be refused, in the same words.

Answers `outcome: PENDING` and `releaseId`: the archive is built, then published. Follow the `PLAYGAMA` publication of [`get_publications`](#get_publications) with that `releaseId`, waiting its `retryAfterSeconds` between reads: `PUBLISHED` — the update is live; `FAILED` — it never went live, players keep the previous build, and no reason is given. The same form may go again, after a new yes. Until the publication ends, another publication of the game, to the catalog or the sandbox, is refused with 409 `PUBLICATION_IN_PROGRESS`.

A call whose answer never arrived may still go in after any read, so the agent never tells the developer it did not. It reads `get_publications` again: a `PLAYGAMA` publication with a new `releaseId` naming this archive is an update of this archive — this call's or another submit's — to follow. Otherwise the outcome is unknown, and the agent says so. It never calls again on the yes it had: a call sends the form as saved when it goes in, which may have changed since. Another call is a new update — read `get_submission_state` again and ask for a new yes.

| Parameter       | Type   | Required | Description                                                                                                                                                                                        |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                                                                                                                                                            |
| `archiveId`     | string | Yes      | An archive of this game whose build check passed (`state.status: PASSED` in `get_archive_status`) — the one `get_submission_state` judged with `reviewType: AUTO`. Its form goes with it, as saved |
| `updateNote`    | string | **Yes**  | What a returning player will notice in this update, 80–500 characters after trimming, under the same rules as `story` (English, no links). Kept with this release and not shown anywhere yet       |

A missing `updateNote`, or one that breaks its rules, is refused with 400 before anything else is checked, and nothing is sent.

A refusal carries `reason` and `message`; the agent relays the message word for word:

| `reason`                  | Status                       | What to do                                                                                                                |
| ------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `AUTO_REVIEW_UNAVAILABLE` | 403                          | The game is not live in the Playgama catalog: only moderation can take it, and the developer submits it in the console    |
| `NO_CHANGES`              | 403                          | The archive and the form are those of the last submit: change the form or choose another archive                          |
| `PUBLICATION_IN_PROGRESS` | 409                          | A publication of this game, to the catalog or the sandbox, is running: wait for it in `get_publications`, then call again |
| any other                 | as in `get_submission_state` | The same refusals and fixes as the submit to moderation: ban, certification, cooldown, the build, the form, the covers    |

## Referrals

Every organization has one permanent invitation link. An organization that accepts an invitation before its first-ever sandbox publication gets a 200-gameplay traffic bonus at once — the welcome bonus; when it then publishes its first sandbox, the organization that invited it gets a 200-gameplay bonus too. These are traffic bonuses — a free sandbox campaign aimed at about 200 gameplays — not cash and not a guaranteed number of players. Each is spent with `start_sandbox_traffic` and its `bonusId`: the agent picks a game, reads `get_sandbox_traffic`, asks the developer and starts it. Reading the program or accepting an invitation never starts traffic by itself.

Bonuses and the invitation belong to the organization, not to a person: every member sees the same link and the same balance.

### get\_referral\_program

The organization's invitation, whether it can still accept one, and its bonuses. There is no enrollment step: every member of the organization can read it and share the link.

| Field                | Meaning                                                                                                                                                                                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `program`            | `acceptingInvitations` — whether new invitations can be accepted right now; `redemptionEnabled` — whether bonuses can be spent on this deployment; `durationDays` — how long a bonus campaign serves                                                               |
| `invitation`         | `code` and `url` — the organization's invitation. Share `url` as given; never build a code or a link by hand                                                                                                                                                       |
| `eligibility`        | `canApply` — whether the organization can still accept an invitation — and `reason` when it cannot: `ALREADY_ATTRIBUTED` (it has accepted one), `ALREADY_PUBLISHED` (it has published a sandbox before), `REFERRAL_CLAIMS_PAUSED`                                  |
| `counts`             | `qualifiedInvites` — invited organizations that have published their first sandbox; `availableBonuses`; `reservedBonuses`                                                                                                                                          |
| `nextAvailableBonus` | The oldest bonus available to spend, or `null`                                                                                                                                                                                                                     |
| `bonuses`            | The newest 100: `id`, `kind` (`WELCOME` or `INVITER`), `status` (`AVAILABLE`; `RESERVED` — its campaign is being started, or the DSP's answer is unknown; `SPENT`), `durationDays`, `runId` (also as `currentRunId`) and `applicationId` once it is tied to a game |
| `nextAction`         | `type`: `CHOOSE_GAME` — a bonus is available, pick a game and start it; `APPLY_CODE` — the organization can still accept an invitation; `INVITE` — share the invitation                                                                                            |

This tool takes no parameters.

### apply\_referral\_code

Accepts an invitation for your organization, from a code or a link the developer gives — the agent never invents one. It is possible until the organization's first-ever sandbox publication. An organization has one inviter, and it cannot be changed later; an organization's own invitation is refused. Grants the welcome bonus without starting a campaign.

Answers `outcome` — `APPLIED`, or `ALREADY_APPLIED` when this same invitation was accepted before, after a lost answer included — and `welcomeBonus`, the same bonus both times. After an uncertain result, the agent reads `get_referral_program`.

| Parameter   | Type   | Required | Description                                                                                                                          |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `codeOrUrl` | string | Yes      | The invitation code (8–32 letters and digits, any case) or the full invitation link, as the developer gave it. Up to 2048 characters |

| `reason`                 | Status | What it means                                                                              |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------ |
| `INVALID_REFERRAL_INPUT` | 422    | Neither a code nor an invitation link of the Playgama developer console                    |
| `REFERRAL_NOT_FOUND`     | 404    | No organization has this invitation                                                        |
| `SELF_REFERRAL`          | 422    | The invitation is your own organization's                                                  |
| `ALREADY_ATTRIBUTED`     | 409    | The organization has already accepted another invitation, and it cannot be replaced        |
| `ALREADY_PUBLISHED`      | 409    | The organization has already published a sandbox, so it can no longer accept an invitation |
| `REFERRAL_CLAIMS_PAUSED` | 503    | New invitations are paused for now; bonuses already granted stay available                 |
