{"openapi":"3.1.0","info":{"title":"Player Two","version":"1.0.0","description":"Player Two (playertwo.lol) is a game store and a launchpad. This is its API for agents and games:\n- Agents: a player's own agent submits and edits that player's games, uploads their pictures, trailers and web builds, and defines a hosted game's trophies and leaderboards, with the player's agent key. The same as tools: the MCP server at /api/mcp.\n- SDK: a hosted game's own calls while it is played (trophies, stats, leaderboards, saves, presence, invites, identity, add-ons, in-game purchases), with its play session's token. Games use them through the SDK script (https://playertwo.lol/docs/sdk), or bundled from npm: playertwo-sdk (init() and the SDK's TypeScript types; room.d.ts for a room's code). A new game from a starter: npm create playertwo-game@latest my-game.\n- Rooms: multiplayer with no server of the game's own (p2.rooms): a seat's ticket, the rooms open now, replays. The rooms themselves are WebSockets on the rooms service the ticket names.\n- Game servers: a game's own server gives trophies and trusted scores, checks who is playing, charges players at its own prices, reads and uses up what they bought, and checks purchase receipts, with the game's key.\n- Studio: a game's services for its developer signed in (its multiplayer among them).\n- Public: the store's games, one game, players in each game now, badges, players' and studios' public pages. No key.\nTimes are milliseconds since 1970 and money is dollars unless a field says otherwise. A refusal is JSON: { error, code? }.\nMore: https://playertwo.lol/llms.txt, https://playertwo.lol/docs#agents, https://playertwo.lol/api/v1/agent/guide.","contact":{"name":"Player Two","url":"https://playertwo.lol/help"}},"servers":[{"url":"https://playertwo.lol"}],"externalDocs":{"description":"The game SDK, rooms included","url":"https://playertwo.lol/docs/sdk"},"tags":[{"name":"Agents","description":"Submit and edit a player's games, with their agent key (p2_agent_...)."},{"name":"MCP","description":"The same, as MCP tools."},{"name":"SDK","description":"A hosted game's calls while it is played, with its play session (p2s_...). npm: playertwo-sdk.","externalDocs":{"url":"https://www.npmjs.com/package/playertwo-sdk"}},{"name":"Rooms","description":"Multiplayer with no server of the game's own: seats in its rooms, its rooms open now and its replays, with its play session (p2s_...)."},{"name":"Game servers","description":"A game's own server, with the game's key (p2g_...)."},{"name":"Studio","description":"A game's services, for its developer signed in on playertwo.lol."},{"name":"Public","description":"What anyone may read."}],"paths":{"/api/v1/agent/guide":{"get":{"operationId":"agentGuide","tags":["Agents"],"summary":"How to submit a game","description":"Every field of a game's page, its limits, the rules, the steps, and an example store game and raise. Anyone may read it.","security":[],"responses":{"200":{"description":"How to submit a game: every field, its limits, the rules, the steps, and an example of each kind.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guide"}}}}}}},"/api/v1/agent/games/check":{"post":{"operationId":"checkDraft","tags":["Agents"],"summary":"Check a draft","description":"The draft as it would be kept, and what is still missing before it can go live. Saves nothing.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftChecked"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games":{"post":{"operationId":"submitGame","tags":["Agents"],"summary":"Submit a game","description":"For the store (kind store) or a raise (kind raise): live at once, or kept as a draft in the studio with publish false. A store game may instead be kept private, or set to come out on a day (release: listed meanwhile with Wishlist in place of Play; its team and testers play it until then). A raise takes no money until its human developer checks their identity in their studio; an agent can't. Five a day.","security":[{"agentKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitBody"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameSubmitted"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}":{"get":{"operationId":"getOwnGame","tags":["Agents"],"summary":"Read one of the player's games","description":"As stored (drafts too), with its raise if it has one.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnGame"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"editGame","tags":["Agents"],"summary":"Change a game's page","description":"Only the fields given change. A raise's terms (goal, smallest stake, share, days, planned price, stages) stand once it is open.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameEdited"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/release":{"put":{"operationId":"setRelease","tags":["Agents"],"summary":"How a game stands before it is out","description":"A store game not out yet: kept private (its page and play only for its team, its testers and Player Two's admins), coming on a day (listed with Wishlist in place of Play; out by itself on its day, its wishlisters told), or out now; its page's countdown, and whether players may ask to playtest it. Once out, it stays out. Its studio's owner or a team admin.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseNow"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/testers":{"get":{"operationId":"listTesters","tags":["Agents"],"summary":"A game's playtesters","description":"Who asked to playtest it from its page (each with what they have played, reviewed and earned on Player Two, to decide by) and who tests it. A tester plays the game before it is out, free and in test mode (p2.test), with every service: rooms, voice, saves, trophies. link: its playtest link (as getPlaytestLink), and each tester's link says they came by it.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TesterList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"actOnTester","tags":["Agents"],"summary":"Invite a playtester, or answer an ask","description":"invite a player by handle (a tester at once, told so), yes or no to a player who asked, or drop one (while its playtest link is on, a dropped tester stays as one said no to, so the link doesn't bring them back). Inviting one who came by the link makes them an invited tester, who stays when the link goes. Up to 1,000 testers a game.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TesterAct"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TesterDone"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/testers/link":{"get":{"operationId":"getPlaytestLink","tags":["Agents"],"summary":"A game's playtest link","description":"Whether anyone with its link plays it as a tester (a game not out yet), and the link while it is on: the game's page with a secret after a #. Off unless turned on.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaytestLink"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"setPlaytestLink","tags":["Agents"],"summary":"Turn a game's playtest link on or off, or reset it","description":"Like a shared document's link: on, anyone who opens it can see the game's page (a private one's too) and, signed in, plays it as a tester (free, in test mode, a paid game too), from then on without the link. Off, or reset (a new link; the old one stops at once), every tester who came by it is taken off; invited testers stay. Turned on again, it is the same link. A game not out yet only (409 out); once it is out, the link is off by itself. 60 changes an hour.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaytestLinkBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaytestLink"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/products":{"get":{"operationId":"listProducts","tags":["Agents"],"summary":"What a game sells","description":"Its add-ons and in-game products (off-sale ones too), each with how many players have it, how many times it was bought and what the studio has made from it.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"defineProduct","tags":["Agents"],"summary":"Add or change a product","description":"By its key. An add-on (dlc) is sold on the game's page, split as its sales (10% Player Two's); a consumable, item, skin, mod or pack is sold inside the game through p2.store, Player Two's own step taking the player's yes (20% Player Two's). Key and kind stay once a player has it.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductInput"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/trophies":{"get":{"operationId":"listTrophies","tags":["Agents"],"summary":"A hosted game's trophies","description":"Every trophy (retired ones too), how many players hold each, the XP budget left of 1,000, and whether the game counts toward players' levels yet.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyDefs"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"defineTrophy","tags":["Agents"],"summary":"Add or change a trophy","description":"By its key. Once a player has earned one, its tier, stat, target and server-only setting stay. The completion trophy (key complete) takes only a sign. The game earns a trophy with the SDK's trophies.unlock(key).","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyInput"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyDefs"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/boards":{"get":{"operationId":"listBoards","tags":["Agents"],"summary":"A hosted game's leaderboards","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardDefs"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"defineBoard","tags":["Agents"],"summary":"Add or change a leaderboard","description":"By its key, up to 20 a game. The game fills it with the SDK's scores.submit(key, value).","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardInput"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardDefs"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/rooms":{"get":{"operationId":"getRooms","tags":["Agents"],"summary":"A game's rooms now","description":"Read-only: its multiplayer rooms open now, by build, and how their players' connections were over the last minute (from what each page says every 10 s; each room tells it every 30 s at most): overall, each room's figures weighed by its players, and its eight busiest rooms' (ping: the middle page's, and p90, in ms there and back; jitter; bytes a second a page each way; corrections and rewinds a minute a page; connections dropped). With whether rooms are on, the live build's room code and the last error that code threw: what the studio's Rooms panel shows. 60 a minute a player. The MCP tool get_rooms answers the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"A game's rooms now and how their connections are: the studio's Rooms panel, with the eight busiest rooms.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRooms"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/web":{"post":{"operationId":"pushStart","tags":["Agents"],"summary":"Push a web build: its manifest","description":"The first of a push's three calls (tools/cli/p2.mjs makes them: https://playertwo.lol/docs/hosting#push-calls): the build's files by path, size and sha256, gzipped if you like (Content-Encoding: gzip). Answered with where to PUT each file the store doesn't hold yet (streamed); then done. With version (p2 push --version 1.4.0; up to 32 characters: letters, digits, dots, dashes, plus signs and spaces, starting with a letter or a digit; 422 bad-version otherwise) the build is called so; left out, by its number in the game's builds (1 for its first: 12). With room: true (p2 push --room), room code alone for a game on its own site that Player Two serves (409 not-followed otherwise).","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushManifest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushStarted"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/web/{build}/done":{"post":{"operationId":"pushDone","tags":["Agents"],"summary":"Push a web build: done","description":"Every file is read back for its hash and size, and the build becomes the game's, playing at https://{slug}.playertwo.cc/ (or, pushed with testers, its testers' build: live false, testers true). Says its version: the manifest's, or its number in the game's builds. Asked again, it says the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"build","in":"path","description":"The build's key, from the manifest's answer.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushDone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/web/builds":{"get":{"operationId":"listBuilds","tags":["Agents"],"summary":"A web game's builds","description":"Every build it has played that is still kept (30 days after another took its place, and the last 20 always), newest first: each one's version (what its developer called it, or its number in the game's builds), who sent each (the player, or which agent key), its size, its files against the build before, its commit and note, the players on it now and where to try it without making it live; and the game's latest changes of build.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildHistory"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"nameBuild","tags":["Agents"],"summary":"Name a build's version","description":"Any kept build of the game (key from listBuilds, live or not) gets the version given, wherever it shows from now on (the studio, listBuilds, its testers' notice): up to 32 characters, letters, digits, dots, dashes, plus signs and spaces, starting with a letter or a digit (422 bad-version otherwise). For a build from a GitHub commit, which comes in called by its number. Nobody is told; in the audit log. The MCP tool name_build does the same. 120 an hour a player, with pushes.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildName"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildNamed"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"makeBuildLive","tags":["Agents"],"summary":"Roll back (or forward) to a build","description":"A build from the game's list becomes its build again (or its testers' build becomes its players'); players get it on their next page load, and rooms opened after it start on its room code. The one live now stays kept, to go back to. Recorded in the game's history with the key that did it and the note. A game following a GitHub repository keeps this build until the repository's next commit.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildPick"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildLive"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/media":{"post":{"operationId":"uploadMedia","tags":["Agents"],"summary":"Upload a picture, a trailer or a font","description":"With game (a slug of the player's): a picture or a video for that game's updates, the only kind of address an update's media takes. Pictures PNG, JPEG or WebP up to 12 MB; trailers and gameplay videos MP4 or WebM up to 80 MB; fonts WOFF2, WOFF, TTF or OTF up to 2 MB. Its address goes in the draft. 100 a day.","security":[{"agentKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUpload"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"415":{"description":"Not a kind of file Player Two takes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/posts":{"post":{"operationId":"postUpdate","tags":["Agents"],"summary":"Post an update on a game's page","description":"First in its page's Updates, as its page's and studio's Post an update make one: a title, the words in Markdown (bold, italic, a heading, lists, a quote, http and https links; no HTML, no pictures by Markdown) and up to four pictures, or a video and three, uploaded for this game (uploadMedia with game; any other address is 422). Its backers, owners, wishlisters and the studio's followers are told in their notices, in plain words, the first 240 characters (none for a private game). With at (5 minutes to 90 days ahead) it waits instead: only its team sees it (listUpdates' scheduled), nobody is told, and at its time it goes up as if posted then (its date that moment) and its audience is told once; up to 20 waiting a game. It can be edited, pinned or deleted after (editUpdate, deleteUpdate), but nobody is told again. 20 a day a player, with the page's own posts. The MCP tool post_update does the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostBody"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"operationId":"listUpdates","tags":["Agents"],"summary":"A game's posted updates","description":"Its posts not deleted (up to 100): the ones waiting to go up first (scheduled, soonest first; only its team sees them), then the pinned one, then the newest: each with its id, kind (devlog: its team wrote it, so it can be edited, pinned and deleted; release, unlock, vote and raise are Player Two's own about the game, which its team edits and pins but can't delete: editable and deletable say so), title, words, attachments, when it went up, when it was edited and when pinned, how many comments it has, how many of them are new for the key's player (since they last read them: markCommentsRead; not their own, not taken down) and whether they are locked. For its team (its studio lists the same under each game's Updates). The MCP tool list_updates answers the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/posts/{id}":{"patch":{"operationId":"editUpdate","tags":["Agents"],"summary":"Edit, pin or unpin a posted update","description":"For its team (whoever may post; not Player Two's admins, who may only delete). title, body and media change any of the game's posts, its team's own (kind devlog) and Player Two's own about the game (a release such as Out now, a raise's news, a stage, a vote: since 2026-11-09; each keeps its kind, its date and its place), by the same rules and limits as posting (media: the whole new list, its attachments uploaded for this game, any other address 422); what is left out stays; nobody is told again; its page says Edited after its date (the time on hover) and its translations are made again. pinned: true pins any post first in the Updates and unpins the one pinned before (one a game); false unpins. A post waiting to go up: at moves its time, now: true posts it now (its audience told then), and editing it isn't marked Edited; pinned waits until it is up (409). Nothing changed answers the post as it is. A file taken out leaves the store a day later unless another post shows it. Recorded in the audit log. 120 an hour a player, with deletes. The MCP tools edit_update and pin_update do the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostEdit"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostRead"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteUpdate","tags":["Agents"],"summary":"Delete a posted update","description":"For its team, or Player Two's admins (moderation): a developer post (kind devlog; Player Two's own are 409) goes from the game's page, the feeds, the studio and the API, unpinned; the notices it sent are taken out of players' lists (an email already sent stays sent); its pictures and video leave the store a day later unless another of the game's posts shows them. It can't be undone; the post is kept out of sight for the audit log. 120 an hour a player, with edits. The MCP tool delete_update does the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/posts/{id}/comments":{"get":{"operationId":"listComments","tags":["Agents"],"summary":"A posted update's comments","description":"Players' comments on one of the player's games' posts, oldest first and the newest last (up to 200), the ones taken down too (removed), each with who wrote it (dev: the game's team), when, the comment it replies to and whether it is new for the key's player (new: since they last read the post's comments, not their own); how many are new, and whether they are locked. Reading doesn't mark them read: markCommentsRead does. Agents read and look after comments, they don't write them. The MCP tool list_comments answers the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"lockComments","tags":["Agents"],"summary":"Lock a post's comments","description":"locked: true, no more comments on that post (the ones there stay); false opens them again. In the audit log. The MCP tool lock_comments does the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentLock"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsLocked"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/comments/read":{"post":{"operationId":"markCommentsRead","tags":["Agents"],"summary":"Mark a game's comments read","description":"For the key's player, on the game's team: a post's comments (post) read up to now (or up to until), or every post's (no post: Mark all read). What each of the team has read is their own: a teammate's new comments stay new. Answers how many posts were marked and how many comments are still new on the game. The MCP tool mark_comments_read does the same. 300 an hour a player.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsRead"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsReadDone"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/games/{slug}/posts/{id}/comments/{cid}":{"delete":{"operationId":"hideComment","tags":["Agents"],"summary":"Take a comment down","description":"Out of sight for players, kept for the team and the admins, in the audit log. The MCP tool hide_comment does the same.","security":[{"agentKey":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"The comment's id, from listComments.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentGone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/builds":{"post":{"operationId":"uploadBuild","tags":["Agents"],"summary":"Upload a web build","description":"A .zip with index.html at its top (up to 100 MB), or one .html page (10 MB). Its address goes in the draft's build. With version, it is called so (the same rule as a push's; 422 bad-version); left out, by its number in its game's builds once a draft names it. Bigger builds are pushed. 20 a day.","security":[{"agentKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildUpload"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"415":{"description":"Not a kind of file Player Two takes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/agent/studio":{"get":{"operationId":"agentStudio","tags":["Agents"],"summary":"The player's studio","description":"Its games and where each stands, and whether the player's identity is checked (needed for raises only).","security":[{"agentKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStudio"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/mcp":{"post":{"operationId":"mcp","tags":["MCP"],"summary":"The MCP server","description":"Model Context Protocol over Streamable HTTP, one JSON-RPC 2.0 message (or a batch) a request. Its tools (get_submission_guide, check_game, upload_media, upload_web_build, submit_game, update_game, get_game, my_studio, list_trophies, define_trophy, list_boards, define_board, list_builds, make_build_live, list_products, define_product, playtest_link, post_update, list_updates, edit_update, pin_update, delete_update, get_rooms) take the agent API's bodies above and do what it does; the agent key goes in Authorization as for the agent API. claude mcp add --transport http player-two https://playertwo.lol/api/mcp --header \"Authorization: Bearer p2_agent_...\"","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpMessage"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpMessage"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/session":{"post":{"operationId":"openSession","tags":["SDK"],"summary":"Start a play session","description":"As a hosted game starts, for the signed-in player: on the web (Player Two's player does it), or from the Player Two app on a computer (with a launch ticket). Its token is the game's Bearer for the calls below.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStart"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionOpened"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"402":{"description":"It costs money: buy it first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/me":{"get":{"operationId":"sdkMe","tags":["SDK"],"summary":"Who is playing","description":"The session's player (their public face and their id for this game), its game, and whether it is a test or a demo.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/beat":{"post":{"operationId":"sdkBeat","tags":["SDK"],"summary":"Still playing","description":"Once a minute, and whenever the game sets its presence: the line friends see, and what they join the player with.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Beat"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Done"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/end":{"post":{"operationId":"sdkEnd","tags":["SDK"],"summary":"The game was closed","description":"The session ends, its time counted.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Done"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/event":{"post":{"operationId":"sdkEvent","tags":["SDK"],"summary":"What the game is doing","description":"loading.done, gameplay.start, gameplay.stop: heard, nothing kept yet.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameEvent"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventHeard"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/air":{"post":{"operationId":"sdkAir","tags":["SDK"],"summary":"On air: the game's live picture for the Plaza","description":"The SDK does this itself in Player Two's player while an online game is played: start offers the game's biggest canvas to Cloudflare Realtime (its answer back), up says the picture is flowing (the Plaza's screens list it from then), check asks now and then whether it still reaches Realtime (410 gone: send it again), stop ends it. Refused (403 off) for a game that isn't online, a studio's test, or a player who turned it off or keeps their activity private.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AirCall"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AirAnswer"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"410":{"description":"Refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/identity":{"get":{"operationId":"sdkIdentity","tags":["SDK"],"summary":"A signed identity for the game's servers","description":"SDK p2.identity(): a JWT for this game only, good for ten minutes, that the game's own servers can trust.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityToken"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/invite":{"post":{"operationId":"sdkInvite","tags":["SDK"],"summary":"A join link","description":"SDK p2.invite(params): the game's page on Player Two, which opens it with the params for whoever follows the link.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invite"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteLink"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/trophies":{"get":{"operationId":"sdkTrophies","tags":["SDK"],"summary":"The game's trophies for its player","description":"Each with whether they hold it and its rarity (a hidden one they don't hold shows no name or way to earn it), and their XP and level.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/trophies/{key}":{"post":{"operationId":"sdkUnlock","tags":["SDK"],"summary":"Earn a trophy","description":"SDK trophies.unlock(key): numbered, with the completion trophy if that is due, and the XP it brought. Not in a session's first ten seconds, nor in a demo, nor a server-only one.","security":[{"playSession":[]}],"parameters":[{"name":"key","in":"path","description":"The trophy's key, as its developer defined it.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyEarned"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/stats":{"get":{"operationId":"sdkStats","tags":["SDK"],"summary":"The player's stats","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"sdkWriteStats","tags":["SDK"],"summary":"Set or add to stats","description":"Whole numbers by lowercase key, each key once a call; earns any trophy waiting on a stat that reached its target.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsSend"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsWritten"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/boards/{key}":{"get":{"operationId":"sdkScores","tags":["SDK"],"summary":"A leaderboard","security":[{"playSession":[]}],"parameters":[{"name":"key","in":"path","description":"The leaderboard's key, as its developer defined it.","required":true,"schema":{"type":"string"}},{"name":"around","in":"query","description":"top (the default) or me: the scores around the player.","required":false,"schema":{"type":"string","enum":["top","me"]}},{"name":"friends","in":"query","description":"1: the player and their friends.","required":false,"schema":{"type":"string","enum":["1"]}},{"name":"limit","in":"query","description":"Rows, 10 by default, up to 100.","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scores"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"sdkSubmitScore","tags":["SDK"],"summary":"Send a score","description":"Kept if it is the player's best. A trusted board takes scores from the game's own server only.","security":[{"playSession":[]}],"parameters":[{"name":"key","in":"path","description":"The leaderboard's key, as its developer defined it.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreSend"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/saves":{"get":{"operationId":"sdkSaves","tags":["SDK"],"summary":"The player's save slots","description":"Each slot's size, version and when it was saved: 8 slots of up to 1 MB. Listing them counts as reading them.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/saves/{slot}":{"get":{"operationId":"sdkSave","tags":["SDK"],"summary":"Read a save","security":[{"playSession":[]}],"parameters":[{"name":"slot","in":"path","description":"The slot's name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveRead"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"sdkPutSave","tags":["SDK"],"summary":"Save","description":"A slot holding a save must be read in this session first (409 read-first); with version, a save made elsewhere since is refused (409 version).","security":[{"playSession":[]}],"parameters":[{"name":"slot","in":"path","description":"The slot's name.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSend"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"sdkDeleteSave","tags":["SDK"],"summary":"Empty a save slot","security":[{"playSession":[]}],"parameters":[{"name":"slot","in":"path","description":"The slot's name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveGone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/mirror/{which}":{"get":{"operationId":"sdkMirror","tags":["SDK"],"summary":"The kept copy of a hosted game's browser storage","description":"The SDK's auto mode on the games host: local (its localStorage) or idb (its IndexedDB).","security":[{"playSession":[]}],"parameters":[{"name":"which","in":"path","description":"local or idb.","required":true,"schema":{"type":"string","enum":["local","idb"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MirrorRead"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"sdkPutMirror","tags":["SDK"],"summary":"Keep a copy of a hosted game's browser storage","security":[{"playSession":[]}],"parameters":[{"name":"which","in":"path","description":"local or idb.","required":true,"schema":{"type":"string","enum":["local","idb"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MirrorSend"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/addons":{"get":{"operationId":"sdkAddons","tags":["SDK"],"summary":"The game's add-ons","description":"Those on sale, each with whether the player has it, and the keys of every add-on they have.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/items":{"get":{"operationId":"sdkItems","tags":["SDK"],"summary":"What the player has from the shop","description":"Player Two's shop: their avatars, skins, wearables, emotes and effects, each with whether they have it on, a Divine one's number and its picture. A game can dress its own character in them.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ItemList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/addons/{key}":{"get":{"operationId":"sdkAddon","tags":["SDK"],"summary":"Whether the player has an add-on","security":[{"playSession":[]}],"parameters":[{"name":"key","in":"path","description":"The add-on's key, as its developer defined it.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonOwned"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/store":{"get":{"operationId":"sdkStore","tags":["SDK"],"summary":"What the game sells inside itself","description":"SDK p2.store.list(): its in-game products on sale (made in its studio), each with whether the player has it (a consumable: how many), and everything of it they have. Refused: 403 guest (nothing is sold to a guest), 429 past 240 reads a minute a session. https://playertwo.lol/docs/sdk#store","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/store/owned":{"get":{"operationId":"sdkStoreOwned","tags":["SDK"],"summary":"What the player has of the game's products","description":"SDK p2.store.owned(). Test purchases only in a test session. Refused: 403 guest, 429 past 240 reads a minute a session.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreOwned"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/store/charges/{id}":{"get":{"operationId":"sdkStoreCharge","tags":["SDK"],"summary":"A charge, as Player Two's card shows it","description":"Player Two's player reads this when the game calls p2.store.pay(id): the charge its server made for this player (what, how much, open, paid, cancelled or expired), and whether this is a test session. Refused: 403 guest, 404 unknown-charge (none by that id for this player in this game), 429 past 240 reads a minute a session.","security":[{"playSession":[]}],"parameters":[{"name":"id","in":"path","description":"The charge's id, from the game's server.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeForPlayer"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/store/receipt":{"post":{"operationId":"sdkStoreReceipt","tags":["SDK"],"summary":"The receipt for a purchase","description":"Player Two's player sends this after its own sheet was paid (p2.store.buy, p2.store.pay: the game never charges): the signed receipt the game is handed, a product's or a charge's. Refused: 404 unknown-purchase (not this player's payment in this game), 409 not-paid (it hasn't gone through), 429 past 60 a minute a session.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreReceiptAsk"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreReceipt"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/store/test":{"post":{"operationId":"sdkStoreTest","tags":["SDK"],"summary":"A test purchase","description":"A test session's (the game's team, a tester) p2.store.buy (a product's key) or p2.store.pay (a charge's id), after its yes on Player Two's sheet: for nothing, marked as a test, with its receipt; a charge paid as a test answers its receipt again. Refused: 403 not-a-test (any other session), 404 unknown-product or unknown-charge, 409 owned (a skin, item or mod they have; real consumables a test can't add to), paid, withdrawn or expired (a charge), 429 past 60 a minute a session.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreTestAsk"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreReceipt"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/cloud":{"get":{"operationId":"sdkCloud","tags":["SDK"],"summary":"A desktop game's saves in the cloud","description":"For the Player Two app: the save folders to sync on this system and the copy kept, with a link to download it.","security":[{"playSession":[]}],"parameters":[{"name":"platform","in":"query","description":"Which system.","required":true,"schema":{"type":"string","enum":["windows","mac","linux"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudSave"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"sdkCloudSend","tags":["SDK"],"summary":"Upload a desktop game's saves","description":"{ size }: where to PUT a new copy (a zip, up to 100 MB). Then { done, size }: it arrived.","security":[{"playSession":[]}],"parameters":[{"name":"platform","in":"query","description":"Which system.","required":true,"schema":{"type":"string","enum":["windows","mac","linux"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudSend"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Too big.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/rooms/ticket":{"post":{"operationId":"sdkRoomSeat","tags":["Rooms"],"summary":"A seat in one of the game's rooms","description":"SDK p2.rooms.join, .create, .party, .world, .watch: the rooms service's address and a ticket good for two minutes, to open the room's WebSocket with. For games hosted on Player Two whose multiplayer is rooms. Refused: 409 rooms-hosted-only (a game on its own site that Player Two doesn't serve), rooms-off (its multiplayer is none or its own servers), no-party, party-too-big, old-build (the page's build is no longer kept, or a world's page isn't on the live build); 422 bad-code, bad-room, bad-teams, bad-ask; 429 (30 seats a minute a session); 503 rooms-stopped (Player Two stopped its rooms). The room itself may still refuse the seat as its socket opens (no-such-room, other-build, full, not-open, not-in-party, too-many-rooms, party-too-big): the SDK rejects with that code. https://playertwo.lol/docs/sdk#rooms","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatAsk"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Seat"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Stopped for now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/rooms/live":{"get":{"operationId":"sdkRoomsLive","tags":["Rooms"],"summary":"The game's public rooms open now","description":"SDK p2.rooms.live: fullest first, of a mode or every mode, to watch one (p2.rooms.watch).","security":[{"playSession":[]}],"parameters":[{"name":"mode","in":"query","description":"Only rooms of this mode.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomsLive"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/rooms/replays":{"get":{"operationId":"sdkReplays","tags":["Rooms"],"summary":"The game's replays","description":"SDK p2.rooms.replays: rooms kept by room.record (or asked to be by their first player), newest first.","security":[{"playSession":[]}],"parameters":[{"name":"limit","in":"query","description":"How many, 30 by default, up to 100.","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Replays"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/rooms/met":{"post":{"operationId":"sdkRoomMet","tags":["Rooms"],"summary":"Who the room had with the player","description":"The SDK, once a minute while in a room: the players (by their ids for this game) the room had with this one for that minute, for the player's Played with recently. Only those in a live session of this game now count, blocked either way left out; only the player's own list is written.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomMet"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetCounted"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/rooms/replay":{"post":{"operationId":"sdkReplay","tags":["Rooms"],"summary":"Where to fetch a replay","description":"SDK p2.rooms.replay: the rooms service's address and a ticket good for two minutes, for the page to fetch the replay with.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayAsk"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Seat"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/avatar":{"get":{"operationId":"sdkAvatar","tags":["SDK"],"summary":"The player's avatar","description":"SDK p2.avatar(): who the player is, to dress the game's character with: their critter's pixels (the same everywhere on Player Two), the same critter as a 3D model (.glb), their name and level, and what they wear. https://playertwo.lol/docs/sdk/travel","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Avatar"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/travel":{"get":{"operationId":"sdkTravelLinks","tags":["SDK"],"summary":"Doors to other games","description":"SDK p2.travel.links(): the Plaza, then up to 12 games for the game's own door menu: where the party's leader is, the games friends are in now, the player's recent ones, the busiest. Only games playable in Player Two's player, out and listed, never this one.","security":[{"playSession":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelLinks"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"sdkTravel","tags":["SDK"],"summary":"Go to another game, or home","description":"SDK p2.travel.to(slug, { params, place }) and p2.travel.home(), which Player Two's player sends (it then swaps the game, the party following): the game gone to and what it is given as p2.params (from, this game's address; place; the game's own), kept as a travel. Refused: 404 no-game, 409 not-out, not-playable, same-game; 422 bad-params.","security":[{"playSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelAsk"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelGone"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/session":{"get":{"operationId":"serverSession","tags":["Game servers"],"summary":"Who is in a play session","description":"Who is in the session a client says it is in, and whether it is live. A session of another game is none here.","security":[{"gameKey":[]}],"parameters":[{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerSession"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/addons":{"get":{"operationId":"serverAddons","tags":["Game servers"],"summary":"The add-ons a player has","description":"By the player's id for this game (?player=), or by the session token a computer's client sends.","security":[{"gameKey":[]}],"parameters":[{"name":"player","in":"query","description":"The player's id for this game.","required":false,"schema":{"type":"string"}},{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerAddons"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/boards/{key}":{"post":{"operationId":"serverScore","tags":["Game servers"],"summary":"Send a player's score","description":"The only way onto a trusted board; kept if it is their best.","security":[{"gameKey":[]}],"parameters":[{"name":"key","in":"path","description":"The leaderboard's key, as its developer defined it.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerScore"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/trophies/{key}":{"post":{"operationId":"serverUnlock","tags":["Game servers"],"summary":"Give a player a trophy","description":"Server-only ones too, naming the player by their id for this game.","security":[{"gameKey":[]}],"parameters":[{"name":"key","in":"path","description":"The trophy's key, as its developer defined it.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerTrophy"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrophyEarned"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store":{"get":{"operationId":"serverStore","tags":["Game servers"],"summary":"What a player has of the game's products","description":"By the player's id for this game (?player=; test purchases counted if their latest session is a test), or by the session token a computer's client sends.","security":[{"gameKey":[]}],"parameters":[{"name":"player","in":"query","description":"The player's id for this game.","required":false,"schema":{"type":"string"}},{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerStore"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store/use":{"post":{"operationId":"serverStoreUse","tags":["Game servers"],"summary":"Use up a player's consumable","description":"n of them (1 if not said), only if they have that many (409 not-enough, with their count); what they have left.","security":[{"gameKey":[]}],"parameters":[{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreUse"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreUsed"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store/receipt":{"post":{"operationId":"serverStoreReceipt","tags":["Game servers"],"summary":"Check a receipt","description":"Whether a receipt a client sent (p2.store.buy, p2.store.pay) is Player Two's, for this game, and what it says: a product's (product, count) or a charge's (charge: id, name, your ref). Answered whatever the token is: read valid; when false, code is malformed, unknown-key, bad-signature, expired (an hour on: read the charge or what the player has instead), wrong-game, not-paid or not-a-receipt.","security":[{"gameKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptCheck"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptChecked"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store/charges":{"post":{"operationId":"serverChargeMake","tags":["Game servers"],"summary":"Charge a player","description":"Your own shop's price, for your own rules: what (name, a line about it) and how much ($0.10 to $500) one player is asked to pay, with your own reference for it (ref). Hand the id to your game's client for p2.store.pay(id): Player Two's card shows the name and price, the player holds to pay, and the money comes to you as an in-game purchase (80% yours, less your backers' share; 20% Player Two's). Open for 30 minutes; paid at most once. The player by their id for this game, or the session token a computer's client sends. Refused: 404 unknown-player, 409 no-game (they don't have the game; a player whose latest session is a test may), not-in-store (archived, taken down, or a raise without a price), 422 bad-name or bad-price, 429 past 60 a minute for one player or 1,200 a minute for the game. https://playertwo.lol/docs/sdk#store-charges","security":[{"gameKey":[]}],"parameters":[{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeInput"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeAnswer"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"operationId":"serverCharges","tags":["Game servers"],"summary":"A player's charges","description":"Newest first, by default those paid: everything a player has bought through your charges, so a server that keeps nothing itself can tell what to give them (each with your ref). Test charges only while the player's latest session is a test. Up to 100 a page; ask again with before=next for more. Refused: 404 unknown-player, 422 bad-state, 429 past 240 a minute for one player.","security":[{"gameKey":[]}],"parameters":[{"name":"player","in":"query","description":"The player's id for this game.","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","description":"paid (if not said), open, cancelled, expired or all.","required":false,"schema":{"type":"string","enum":["paid","open","cancelled","expired","all"]}},{"name":"limit","in":"query","description":"1 to 100 (100 if not said).","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"before","in":"query","description":"Only charges made before this (milliseconds): the last page's next.","required":false,"schema":{"type":"integer"}},{"name":"x-player-session","in":"header","description":"The play session token (p2s_...) the game's client sent its server.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeList"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store/charges/{id}":{"get":{"operationId":"serverCharge","tags":["Game servers"],"summary":"One of your charges","description":"Whether it's open, paid (with the payment, and whether as a test), cancelled or expired. Refused: 404 unknown-charge.","security":[{"gameKey":[]}],"parameters":[{"name":"id","in":"path","description":"The charge's id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeAnswer"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/store/charges/{id}/cancel":{"post":{"operationId":"serverChargeCancel","tags":["Game servers"],"summary":"Withdraw a charge","description":"Before it's paid: it can't be paid from then on (a payment already on its way is given back to the player). Asked again, the same answer. Refused: 404 unknown-charge, 409 paid.","security":[{"gameKey":[]}],"parameters":[{"name":"id","in":"path","description":"The charge's id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeAnswer"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/identity":{"post":{"operationId":"serverIdentity","tags":["Game servers"],"summary":"Check an identity","description":"Whether an identity a client sent is good for this game, and who it names. Answered whatever the token is: read valid.","security":[{"gameKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityCheck"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityChecked"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/sdk/server/match":{"post":{"operationId":"serverMatch","tags":["Game servers"],"summary":"Report a match","description":"A match the game's server ran, and who played in it: each by the identity their client sent (p2.identity(), taken up to six hours past its time), their play session's token, or their id for this game (a player of it in the last six hours). Each player's Played with recently on Player Two gets the others; guests and players blocked either way are left out. With id, a match is counted once (409 reported).","security":[{"gameKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchReport"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchCounted"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/travel":{"get":{"operationId":"partyTravel","tags":["SDK"],"summary":"Where the party's leader went","description":"For Player Two's player while a game is open: whether the player leads a party, and where its leader went in the last two minutes (a game, with what to follow them with, or the Plaza).","security":[{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartyTravel"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"portalIn","tags":["SDK"],"summary":"A game walked into","description":"From one of the Plaza's screens, or after the party's leader: kept as a portal (Player Two's own trophy Through five portals).","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalIn"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCounted"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/services":{"get":{"operationId":"getServices","tags":["Studio"],"summary":"A game's services","description":"For its developer and their team: its trophies with the XP budget, its leaderboards, its game keys, Connect, and its multiplayer: whether rooms are on, the live build's room code, the rooms open now.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServicesView"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"changeServices","tags":["Studio"],"summary":"Change a game's services","description":"One change a request: a trophy or a leaderboard, a game key, Connect, or its multiplayer ({ do: \"multiplayer\", value: \"rooms\" | \"none\" | \"own\" }). Answered with the services as they are now. An agent sets multiplayer in the draft instead.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAct"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServicesView"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/posts":{"post":{"operationId":"teamPostUpdate","tags":["Studio"],"summary":"Post an update (signed in)","description":"What its page's and studio's Post an update send: as postUpdate, for its team signed in.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostBody"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"operationId":"teamListUpdates","tags":["Studio"],"summary":"A game's posted updates (signed in)","description":"As listUpdates, for its team signed in.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/posts/{id}":{"patch":{"operationId":"teamEditUpdate","tags":["Studio"],"summary":"Edit, pin or unpin a posted update (signed in)","description":"What a post's own Edit and Pin on its game's page send: as editUpdate, for its team signed in.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostEdit"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostRead"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"teamDeleteUpdate","tags":["Studio"],"summary":"Delete a posted update (signed in)","description":"What a post's own Delete on its game's page sends, once confirmed: as deleteUpdate, for its team or an admin signed in.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/posts/{id}/comments":{"get":{"operationId":"readComments","tags":["Public"],"summary":"A posted update's comments","description":"Anyone reads them, oldest first and the newest last (up to 200); signed in, with whether the player may comment (can) and look after them (keeper: its team and the admins, who see the ones taken down); for its team (team), which are new for them (new) and how many. Reading doesn't mark them read: teamMarkCommentsRead does.","security":[{"session":[]},{}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentList"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"writeComment","tags":["Public"],"summary":"Comment on a posted update","description":"A signed-in player who isn't banned: 1 to 2,000 characters, or a reply to one of its comments (reply). Refused (403) for a player blocked by the studio's owner or blocking them, or by the one they reply to; 409 once its comments are locked. 60 comments and Discussion answers an hour a player. The post's author is told in one notice a post that counts up while unread; the one replied to is told.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentBody"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentKept"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"It can't be done in the state things are in (code says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"A value isn't right (field or missing says which).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"teamLockComments","tags":["Studio"],"summary":"Lock a post's comments (signed in)","description":"As lockComments, for its team or an admin signed in.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentLock"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsLocked"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/comments/read":{"post":{"operationId":"teamMarkCommentsRead","tags":["Studio"],"summary":"Mark a game's comments read (signed in)","description":"As markCommentsRead, for its team signed in: the studio and the game's page send it as a post's comments are opened, and the studio's Mark all read.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsRead"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentsReadDone"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games/{slug}/posts/{id}/comments/{cid}":{"delete":{"operationId":"dropComment","tags":["Public"],"summary":"Take a comment down (signed in)","description":"Its writer takes their own down; the game's team and the admins anyone's.","security":[{"session":[]}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"The post's id, from listUpdates.","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"The comment's id, from listComments.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentGone"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/games":{"get":{"operationId":"listGames","tags":["Public"],"summary":"Every game","description":"Every game in the store and every raise, as their pages show them. The same for everyone; kept half a minute at the edge.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameList"}}}}}}},"/api/v1/games/{slug}":{"get":{"operationId":"getGame","tags":["Public"],"summary":"One game","description":"Its page as its developer wrote it (draft) and its state on the platform (real). An archived game only for those who may open it.","security":[{"session":[]},{}],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GameRead"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/live":{"get":{"operationId":"liveCounts","tags":["Public"],"summary":"Players in each game now","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveCounts"}}}}}}},"/api/v1/items":{"get":{"operationId":"getShop","tags":["Public"],"summary":"The shop","description":"Player Two's own items (avatars, skins, wearables, emotes, effects) at their tiers' prices: Common free at a level, then $0.99, $1.99, $5.99, $9.99 and $19.99 (Divine, 77 of each). For a signed-in player, which are theirs and what they have on; and the games' mods on sale. Bought as a payment (purpose item).","security":[{"session":[]},{}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shop"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"wearItems","tags":["Public"],"summary":"Put on what you own","description":"An avatar, a skin, a wearable on each slot and an effect, all your own (anything else is refused). Seen in the Plaza and by games.","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WornBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WornBody"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"No key or token, or one that isn't good.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not allowed: not this player's, a demo, or the game's server only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/items/worn":{"get":{"operationId":"wornBy","tags":["Public"],"summary":"What players have on","description":"By handle (?h=a,b,c; 100 at most), with the emotes each may do: only what they own.","security":[],"parameters":[{"name":"h","in":"query","description":"Handles, comma separated.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WornPlayers"}}}},"400":{"description":"The body didn't match its schema (issues says where), or something it needs is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/items/{key}/picture":{"get":{"operationId":"itemPicture","tags":["Public"],"summary":"An item's picture","description":"Its own pixels as SVG. Emotes and effects have none.","security":[],"parameters":[{"name":"key","in":"path","description":"The item's key.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The picture.","content":{"image/svg+xml":{"schema":{"type":"string"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/critters/{file}":{"get":{"operationId":"critterModel","tags":["Public"],"summary":"A critter as a 3D model","description":"{name}.glb: the critter a name gives everywhere on Player Two (p2.avatar()'s critter.model), as a glTF binary: a pixel 7 cm a side, standing on the origin, facing +Z, coloured by its vertices. From anywhere (CORS *); it never changes.","security":[],"parameters":[{"name":"file","in":"path","description":"The critter's name, then .glb.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The model.","content":{"model/gltf-binary":{"schema":{"type":"string"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/badge/{slug}":{"get":{"operationId":"gameBadge","tags":["Public"],"summary":"A game's badge","description":"A picture (SVG) with its live figure, for a README or a press kit, linking to its page.","security":[],"parameters":[{"name":"slug","in":"path","description":"The game's address on Player Two (playertwo.lol/game/{slug}).","required":true,"schema":{"type":"string"}},{"name":"kind","in":"query","description":"What it says, as the embedded card's.","required":false,"schema":{"type":"string"}},{"name":"theme","in":"query","description":"light for a light badge.","required":false,"schema":{"type":"string","enum":["light"]}}],"responses":{"200":{"description":"The badge.","content":{"image/svg+xml":{"schema":{"type":"string"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/players/{handle}":{"get":{"operationId":"getPlayer","tags":["Public"],"summary":"A player's public page","description":"Their name, level, standing unless they keep off the leaderboards, who follows them, and what they did lately unless they keep it private. Never an email, anything about their balance, or an id.","security":[{"session":[]},{}],"parameters":[{"name":"handle","in":"path","description":"Their handle.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPublic"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Too many: wait a little.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/studios/{handle}":{"get":{"operationId":"getStudio","tags":["Public"],"summary":"A studio","description":"Its profile, GitHub activity and followers. Its games are in /api/v1/games.","security":[],"parameters":[{"name":"handle","in":"path","description":"The studio's handle.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioRead"}}}},"404":{"description":"There is no such thing (or it isn't this player's).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"schemas":{"ApiError":{"type":"object","properties":{"error":{"type":"string","description":"What was wrong, in words a person can read."},"code":{"description":"A stable code for what was wrong, when there is one (session-ended, unknown-board, read-first...).","type":"string"},"issues":{"description":"A body that didn't match its schema: each problem, as path: message.","type":"array","items":{"type":"string"}},"field":{"description":"The field at fault.","type":"string"}},"required":["error"],"additionalProperties":{}},"GamePlayer":{"type":"object","properties":{"id":{"type":"string","description":"The player's id for this game only: the same every session, different in every game."},"handle":{"type":"string"},"name":{"type":"string"},"picture":{"type":["string","null"]},"creature":{"type":"string","description":"The seed of their pixel creature (their handle, unless they chose another)."},"level":{"type":"integer"}},"required":["id","handle","name","picture","creature","level"]},"Draft":{"type":"object","properties":{"v":{"type":"number","const":1},"kind":{"description":"\"store\": sold (or free) and played or downloaded from its page. \"raise\": players back a game being made. Default raise.","type":"string","enum":["store","raise"]},"title":{"type":"string","maxLength":60,"description":"The game's name. Up to 60 characters; longer is cut."},"tagline":{"type":"string","maxLength":90,"description":"One line about it. Up to 90 characters; longer is cut."},"pitch":{"type":"string","maxLength":700,"description":"What it is, in a paragraph. Up to 700 characters; longer is cut."},"more":{"type":"string","maxLength":700,"description":"A second paragraph. Up to 700 characters; longer is cut."},"qualifier":{"type":"string","maxLength":40,"description":"A short note after the tagline, like \"an unofficial fan game\". Up to 40 characters; longer is cut."},"genres":{"description":"Up to 10, the main ones first (a game shows its first four), each up to 24 characters. The store's: Action, Adventure, Arcade, Arena, Casual, Co-op, Fighting, Horror, Multiplayer, Platformer, Puzzle, Racing, Roguelite, RPG, Shooter, Simulation, Social, Sports, Strategy, Survival.","type":"array","items":{"type":"string"}},"modes":{"description":"Up to 10 short words (Duel, Co-op...), the main ones first.","type":"array","items":{"type":"string"}},"platforms":{"description":"Where it plays.","type":"array","items":{"type":"string","enum":["Browser","Windows","Mac","Linux","Mobile"]}},"controller":{"description":"\"full\": it all plays with a gamepad. \"partial\": some of it needs a keyboard or mouse.","type":"string","enum":["full","partial",""]},"orientation":{"description":"Which way a phone is held to play it: \"landscape\" (sideways) or \"portrait\" (upright); \"\" either way. A landscape game's card on X plays it sideways on a phone held upright, as X's app doesn't turn.","type":"string","enum":["landscape","portrait",""]},"requirements":{"description":"What a computer needs for its downloads: the least, and what plays it best.","type":"object","properties":{"min":{"type":"object","properties":{"os":{"type":"string","maxLength":80,"description":"Operating system. Up to 80 characters; longer is cut."},"cpu":{"type":"string","maxLength":80,"description":"Processor. Up to 80 characters; longer is cut."},"gpu":{"type":"string","maxLength":80,"description":"Graphics. Up to 80 characters; longer is cut."},"ram":{"type":"string","maxLength":80,"description":"Memory. Up to 80 characters; longer is cut."},"storage":{"type":"string","maxLength":80,"description":"Disk space. Up to 80 characters; longer is cut."},"notes":{"type":"string","maxLength":200,"description":"Anything else. Up to 200 characters; longer is cut."}}},"rec":{"type":"object","properties":{"os":{"type":"string","maxLength":80,"description":"Operating system. Up to 80 characters; longer is cut."},"cpu":{"type":"string","maxLength":80,"description":"Processor. Up to 80 characters; longer is cut."},"gpu":{"type":"string","maxLength":80,"description":"Graphics. Up to 80 characters; longer is cut."},"ram":{"type":"string","maxLength":80,"description":"Memory. Up to 80 characters; longer is cut."},"storage":{"type":"string","maxLength":80,"description":"Disk space. Up to 80 characters; longer is cut."},"notes":{"type":"string","maxLength":200,"description":"Anything else. Up to 200 characters; longer is cut."}}}}},"age":{"description":"Who it is for. Sexual content or nudity in content makes it 18 whatever is given.","type":"string","enum":["13","16","18","all",""]},"content":{"description":"What it shows, for players and parents.","type":"array","items":{"type":"string","enum":["Violence","Blood and gore","Sexual content","Nudity","Strong language","Drugs, alcohol or tobacco","Gambling-style play","Frightening scenes","Talking with strangers online"]}},"languages":{"description":"The languages it comes in: its words on screen (ui), voices (audio), subtitles (subs).","type":"array","items":{"type":"object","properties":{"lang":{"type":"string","enum":["English","Spanish","Spanish (Latin America)","Portuguese (Brazil)","Portuguese","French","German","Italian","Dutch","Polish","Russian","Ukrainian","Turkish","Arabic","Hebrew","Hindi","Bengali","Thai","Vietnamese","Indonesian","Malay","Filipino","Japanese","Korean","Chinese (Simplified)","Chinese (Traditional)","Swedish","Norwegian","Danish","Finnish","Czech","Slovak","Hungarian","Romanian","Bulgarian","Greek","Persian"]},"ui":{"type":"boolean"},"audio":{"type":"boolean"},"subs":{"type":"boolean"}},"required":["lang"]}},"access":{"description":"What it does for players with a disability.","type":"array","items":{"type":"string","enum":["Subtitles","Captions for sounds","Adjustable text size","Colour-blind options","High contrast","Remappable controls","One-handed play","No timed inputs needed","Adjustable difficulty","Save anytime","Separate volume controls","Narrated menus","Camera comfort options","Mouse only","Keyboard only","Touch only"]}},"name":{"type":"string","maxLength":28,"description":"The name as lettered on the card (uppercase); the title when empty. Up to 28 characters; longer is cut."},"sub":{"type":"string","maxLength":28,"description":"A line under the name. Up to 28 characters; longer is cut."},"font":{"description":"How the name is lettered.","anyOf":[{"type":"string","description":"A template (anton, bungee, bangers, cinzel, syncopate, kanit, pixel, archivo, chakra), or a font's address from upload_media."},{"type":"object","properties":{"google":{"type":"string"},"weight":{"type":"number"},"italic":{"type":"boolean"}},"required":["google"],"description":"A Google Fonts family at one of its weights: its files are kept in Player Two's own store."}]},"face":{"description":"The typeface the name is lettered in, as Player Two keeps it (made from font).","type":"object","properties":{"from":{"type":"string","enum":["google","own"]},"family":{"type":"string"},"weight":{"type":"number"},"italic":{"type":"boolean"},"files":{"type":"array","items":{"type":"object","properties":{"src":{"type":"string"},"range":{"type":"string"}},"required":["src"],"additionalProperties":{}}}},"required":["from","family","weight","italic","files"],"additionalProperties":{}},"size":{"description":"The lettering's size, 0.5 to 1.6.","type":"number"},"spacing":{"type":"string","maxLength":10,"description":"Letter spacing, as CSS. Up to 10 characters; longer is cut."},"glow":{"pattern":"^#[0-9a-fA-F]{6}$","description":"The game's colour, #rrggbb.","type":"string"},"art":{"type":"string","description":"The key art from upload_media, required: square, 2400 by 2400 at best, no title on it. The game's card (its middle two-thirds), its page's first screen in 3D and its wide picture in the store."},"tall":{"type":"string","description":"An older listing's vertical poster (2:3): its card while it has no key art."},"depth":{"type":"string","description":"The key art's depth map, made by Player Two."},"poster":{"type":"string","description":"An older listing's main poster (16:9): its wide picture beside key art; new key art takes the place of both posters."},"clip":{"type":"string","description":"The launch trailer from upload_media, required (MP4 or WebM)."},"sound":{"description":"Whether the trailer's sound may play.","type":"boolean"},"videos":{"description":"Up to 4 gameplay videos from upload_media, after the trailer, in this order.","type":"array","items":{"type":"string"}},"shots":{"description":"Up to 8 screenshots from upload_media.","type":"array","items":{"type":"string"}},"copies":{"description":"The pictures' smaller copies, by each picture's address (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"w":{"type":"number"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"number"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"clipCopies":{"description":"The trailer's lighter copies (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"object","properties":{"h":{"type":"number"},"src":{"type":"string"}},"required":["h","src"]}}},"stills":{"description":"A still of each feature's video (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"playUrl":{"type":"string","description":"The https address where the game is played on its own site."},"build":{"type":"string","description":"The address upload_web_build returned: a web build Player Two keeps, played at https://<slug>.playertwo.cc."},"follow":{"description":"With playUrl (on for a new game unless false): Player Two serves the site from https://<slug>.playertwo.cc, so saves follow players and the SDK is in its pages.","type":"boolean"},"multiplayer":{"description":"For a game hosted on Player Two: Player Two's rooms through p2.rooms (the default), single-player, or its own servers.","type":"string","enum":["rooms","none","own"]},"agentNotes":{"$ref":"#/components/schemas/AgentNotes"},"price":{"minimum":0,"maximum":500,"description":"Store: dollars, 0 is free.","type":"number"},"demo":{"description":"Store: this is a free demo, not the finished game.","type":"boolean"},"trial":{"enum":[0,15,30,60,90,120],"description":"Store, for a game that costs money and plays in the browser: a free trial in minutes, once for each player who doesn't own it (0: none; a new game left without one gets 30).","type":"number"},"features":{"description":"Store: up to 6.","type":"array","items":{"anyOf":[{"type":"string","maxLength":90,"description":"A feature with neither picture nor video: words on the page, in the game's colour. Up to 90 characters; longer is cut."},{"type":"object","properties":{"text":{"type":"string","maxLength":90,"description":"The feature, in a line. Up to 90 characters; longer is cut."},"art":{"type":"string","description":"A picture's address from upload_media, shown wide (16:9)."},"clip":{"type":"string","description":"Or a video's address from upload_media (a picture wins when both are given)."}}}]}},"goal":{"minimum":1,"maximum":10000000,"description":"Raise: dollars asked.","type":"number"},"minTicket":{"description":"Raise: the smallest stake, dollars.","type":"number"},"backers":{"description":"Raise: what backers get, a share of every sale or rewards only.","type":"string","enum":["share","rewards"]},"rewards":{"description":"Raise with backers \"rewards\": up to 8, by the least put in for each.","type":"array","items":{"type":"object","properties":{"min":{"type":"number"},"title":{"type":"string","maxLength":60,"description":"What they get. Up to 60 characters; longer is cut."},"body":{"type":"string","maxLength":240,"description":"A line more. Up to 240 characters; longer is cut."}},"required":["min","title"]}},"share":{"minimum":0.4,"maximum":0.85,"description":"Raise with backers \"share\": the backers' share of every sale.","type":"number"},"days":{"minimum":3,"maximum":120,"description":"Raise: how long it is open.","type":"number"},"opens":{"description":"Raise: when it opens to backers (milliseconds since 1970), up to 60 days ahead.","type":"number"},"plannedPrice":{"description":"Raise: the price it will sell at, dollars.","type":"number"},"today":{"description":"Raise: where the game is today.","type":"string","enum":["concept","prototype","demo","early"]},"todayLine":{"type":"string","maxLength":90,"description":"Raise: where it is today, in a line. Up to 90 characters; longer is cut."},"why":{"type":"string","maxLength":300,"description":"Raise: why back it. Up to 300 characters; longer is cut."},"stages":{"description":"Raise: 1 to 8 stages.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":60,"description":"The stage. Up to 60 characters; longer is cut."},"proof":{"type":"string","maxLength":240,"description":"What backers will see. Up to 240 characters; longer is cut."},"share":{"description":"Its share of the raise, 0 to 1; all add up to 1.","type":"number"},"days":{"description":"Due this many days after the raise is funded.","type":"number"}},"required":["title"]}},"branches":{"description":"Raise: what the money builds, up to 4 branches of up to 3 goals.","type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":30,"description":"A branch of work. Up to 30 characters; longer is cut."},"goals":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"now":{"type":"string"},"next":{"type":"string"},"cost":{"type":"number"},"stage":{"type":"number"},"shot":{"type":"number"}},"required":["title"]}}},"required":["name","goals"]}},"studio":{"type":"string","maxLength":40,"description":"The studio's name, required. Up to 40 characters; longer is cut."},"site":{"type":"string","description":"Its website."},"base":{"type":"string","maxLength":40,"description":"What it is, like \"Independent studio\". Up to 40 characters; longer is cut."},"note":{"type":"string","maxLength":900,"description":"A note to players. Up to 900 characters; longer is cut."},"sign":{"type":"string","maxLength":40,"description":"How it signs off. Up to 40 characters; longer is cut."},"x":{"description":"X handle or link.","type":"string"},"discord":{"description":"Discord invite.","type":"string"},"youtube":{"description":"YouTube channel.","type":"string"},"postTitle":{"type":"string","maxLength":80,"description":"The first post's title. Up to 80 characters; longer is cut."},"postBody":{"type":"string","maxLength":600,"description":"The first post. Up to 600 characters; longer is cut."}},"additionalProperties":{}},"DraftKept":{"type":"object","properties":{"v":{"type":"number","const":1},"kind":{"description":"\"store\": sold (or free) and played or downloaded from its page. \"raise\": players back a game being made. Default raise.","type":"string","enum":["store","raise"]},"title":{"type":"string","maxLength":60,"description":"The game's name. Up to 60 characters; longer is cut."},"tagline":{"type":"string","maxLength":90,"description":"One line about it. Up to 90 characters; longer is cut."},"pitch":{"type":"string","maxLength":700,"description":"What it is, in a paragraph. Up to 700 characters; longer is cut."},"more":{"type":"string","maxLength":700,"description":"A second paragraph. Up to 700 characters; longer is cut."},"qualifier":{"type":"string","maxLength":40,"description":"A short note after the tagline, like \"an unofficial fan game\". Up to 40 characters; longer is cut."},"genres":{"description":"Up to 10, the main ones first (a game shows its first four), each up to 24 characters. The store's: Action, Adventure, Arcade, Arena, Casual, Co-op, Fighting, Horror, Multiplayer, Platformer, Puzzle, Racing, Roguelite, RPG, Shooter, Simulation, Social, Sports, Strategy, Survival.","type":"array","items":{"type":"string"}},"modes":{"description":"Up to 10 short words (Duel, Co-op...), the main ones first.","type":"array","items":{"type":"string"}},"platforms":{"description":"Where it plays.","type":"array","items":{"type":"string","enum":["Browser","Windows","Mac","Linux","Mobile"]}},"controller":{"description":"\"full\": it all plays with a gamepad. \"partial\": some of it needs a keyboard or mouse.","type":"string","enum":["full","partial",""]},"orientation":{"description":"Which way a phone is held to play it: \"landscape\" (sideways) or \"portrait\" (upright); \"\" either way. A landscape game's card on X plays it sideways on a phone held upright, as X's app doesn't turn.","type":"string","enum":["landscape","portrait",""]},"requirements":{"description":"What a computer needs for its downloads: the least, and what plays it best.","type":"object","properties":{"min":{"type":"object","properties":{"os":{"type":"string","maxLength":80,"description":"Operating system. Up to 80 characters; longer is cut."},"cpu":{"type":"string","maxLength":80,"description":"Processor. Up to 80 characters; longer is cut."},"gpu":{"type":"string","maxLength":80,"description":"Graphics. Up to 80 characters; longer is cut."},"ram":{"type":"string","maxLength":80,"description":"Memory. Up to 80 characters; longer is cut."},"storage":{"type":"string","maxLength":80,"description":"Disk space. Up to 80 characters; longer is cut."},"notes":{"type":"string","maxLength":200,"description":"Anything else. Up to 200 characters; longer is cut."}}},"rec":{"type":"object","properties":{"os":{"type":"string","maxLength":80,"description":"Operating system. Up to 80 characters; longer is cut."},"cpu":{"type":"string","maxLength":80,"description":"Processor. Up to 80 characters; longer is cut."},"gpu":{"type":"string","maxLength":80,"description":"Graphics. Up to 80 characters; longer is cut."},"ram":{"type":"string","maxLength":80,"description":"Memory. Up to 80 characters; longer is cut."},"storage":{"type":"string","maxLength":80,"description":"Disk space. Up to 80 characters; longer is cut."},"notes":{"type":"string","maxLength":200,"description":"Anything else. Up to 200 characters; longer is cut."}}}}},"age":{"description":"Who it is for. Sexual content or nudity in content makes it 18 whatever is given.","type":"string","enum":["13","16","18","all",""]},"content":{"description":"What it shows, for players and parents.","type":"array","items":{"type":"string","enum":["Violence","Blood and gore","Sexual content","Nudity","Strong language","Drugs, alcohol or tobacco","Gambling-style play","Frightening scenes","Talking with strangers online"]}},"languages":{"description":"The languages it comes in: its words on screen (ui), voices (audio), subtitles (subs).","type":"array","items":{"type":"object","properties":{"lang":{"type":"string","enum":["English","Spanish","Spanish (Latin America)","Portuguese (Brazil)","Portuguese","French","German","Italian","Dutch","Polish","Russian","Ukrainian","Turkish","Arabic","Hebrew","Hindi","Bengali","Thai","Vietnamese","Indonesian","Malay","Filipino","Japanese","Korean","Chinese (Simplified)","Chinese (Traditional)","Swedish","Norwegian","Danish","Finnish","Czech","Slovak","Hungarian","Romanian","Bulgarian","Greek","Persian"]},"ui":{"type":"boolean"},"audio":{"type":"boolean"},"subs":{"type":"boolean"}},"required":["lang"]}},"access":{"description":"What it does for players with a disability.","type":"array","items":{"type":"string","enum":["Subtitles","Captions for sounds","Adjustable text size","Colour-blind options","High contrast","Remappable controls","One-handed play","No timed inputs needed","Adjustable difficulty","Save anytime","Separate volume controls","Narrated menus","Camera comfort options","Mouse only","Keyboard only","Touch only"]}},"name":{"type":"string","maxLength":28,"description":"The name as lettered on the card (uppercase); the title when empty. Up to 28 characters; longer is cut."},"sub":{"type":"string","maxLength":28,"description":"A line under the name. Up to 28 characters; longer is cut."},"font":{"description":"How the name is lettered.","anyOf":[{"type":"string","description":"A template (anton, bungee, bangers, cinzel, syncopate, kanit, pixel, archivo, chakra), or a font's address from upload_media."},{"type":"object","properties":{"google":{"type":"string"},"weight":{"type":"number"},"italic":{"type":"boolean"}},"required":["google"],"description":"A Google Fonts family at one of its weights: its files are kept in Player Two's own store."}]},"face":{"description":"The typeface the name is lettered in, as Player Two keeps it (made from font).","type":"object","properties":{"from":{"type":"string","enum":["google","own"]},"family":{"type":"string"},"weight":{"type":"number"},"italic":{"type":"boolean"},"files":{"type":"array","items":{"type":"object","properties":{"src":{"type":"string"},"range":{"type":"string"}},"required":["src"],"additionalProperties":{}}}},"required":["from","family","weight","italic","files"],"additionalProperties":{}},"size":{"description":"The lettering's size, 0.5 to 1.6.","type":"number"},"spacing":{"type":"string","maxLength":10,"description":"Letter spacing, as CSS. Up to 10 characters; longer is cut."},"glow":{"pattern":"^#[0-9a-fA-F]{6}$","description":"The game's colour, #rrggbb.","type":"string"},"art":{"type":"string","description":"The key art from upload_media, required: square, 2400 by 2400 at best, no title on it. The game's card (its middle two-thirds), its page's first screen in 3D and its wide picture in the store."},"tall":{"type":"string","description":"An older listing's vertical poster (2:3): its card while it has no key art."},"depth":{"type":"string","description":"The key art's depth map, made by Player Two."},"poster":{"type":"string","description":"An older listing's main poster (16:9): its wide picture beside key art; new key art takes the place of both posters."},"clip":{"type":"string","description":"The launch trailer from upload_media, required (MP4 or WebM)."},"sound":{"description":"Whether the trailer's sound may play.","type":"boolean"},"videos":{"description":"Up to 4 gameplay videos from upload_media, after the trailer, in this order.","type":"array","items":{"type":"string"}},"shots":{"description":"Up to 8 screenshots from upload_media.","type":"array","items":{"type":"string"}},"copies":{"description":"The pictures' smaller copies, by each picture's address (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"w":{"type":"number"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"number"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"clipCopies":{"description":"The trailer's lighter copies (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"object","properties":{"h":{"type":"number"},"src":{"type":"string"}},"required":["h","src"]}}},"stills":{"description":"A still of each feature's video (made by Player Two).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"playUrl":{"type":"string","description":"The https address where the game is played on its own site."},"build":{"type":"string","description":"The address upload_web_build returned: a web build Player Two keeps, played at https://<slug>.playertwo.cc."},"follow":{"description":"With playUrl (on for a new game unless false): Player Two serves the site from https://<slug>.playertwo.cc, so saves follow players and the SDK is in its pages.","type":"boolean"},"multiplayer":{"description":"For a game hosted on Player Two: Player Two's rooms through p2.rooms (the default), single-player, or its own servers.","type":"string","enum":["rooms","none","own"]},"agentNotes":{"$ref":"#/components/schemas/AgentNotes"},"price":{"minimum":0,"maximum":500,"description":"Store: dollars, 0 is free.","type":"number"},"demo":{"description":"Store: this is a free demo, not the finished game.","type":"boolean"},"trial":{"enum":[0,15,30,60,90,120],"description":"Store, for a game that costs money and plays in the browser: a free trial in minutes, once for each player who doesn't own it (0: none; a new game left without one gets 30).","type":"number"},"features":{"description":"Store: up to 6.","type":"array","items":{"anyOf":[{"type":"string","maxLength":90,"description":"A feature with neither picture nor video: words on the page, in the game's colour. Up to 90 characters; longer is cut."},{"type":"object","properties":{"text":{"type":"string","maxLength":90,"description":"The feature, in a line. Up to 90 characters; longer is cut."},"art":{"type":"string","description":"A picture's address from upload_media, shown wide (16:9)."},"clip":{"type":"string","description":"Or a video's address from upload_media (a picture wins when both are given)."}}}]}},"goal":{"minimum":1,"maximum":10000000,"description":"Raise: dollars asked.","type":"number"},"minTicket":{"description":"Raise: the smallest stake, dollars.","type":"number"},"backers":{"description":"Raise: what backers get, a share of every sale or rewards only.","type":"string","enum":["share","rewards"]},"rewards":{"description":"Raise with backers \"rewards\": up to 8, by the least put in for each.","type":"array","items":{"type":"object","properties":{"min":{"type":"number"},"title":{"type":"string","maxLength":60,"description":"What they get. Up to 60 characters; longer is cut."},"body":{"type":"string","maxLength":240,"description":"A line more. Up to 240 characters; longer is cut."}},"required":["min","title"]}},"share":{"minimum":0.4,"maximum":0.85,"description":"Raise with backers \"share\": the backers' share of every sale.","type":"number"},"days":{"minimum":3,"maximum":120,"description":"Raise: how long it is open.","type":"number"},"opens":{"description":"Raise: when it opens to backers (milliseconds since 1970), up to 60 days ahead.","type":"number"},"plannedPrice":{"description":"Raise: the price it will sell at, dollars.","type":"number"},"today":{"description":"Raise: where the game is today.","type":"string","enum":["concept","prototype","demo","early"]},"todayLine":{"type":"string","maxLength":90,"description":"Raise: where it is today, in a line. Up to 90 characters; longer is cut."},"why":{"type":"string","maxLength":300,"description":"Raise: why back it. Up to 300 characters; longer is cut."},"stages":{"description":"Raise: 1 to 8 stages.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":60,"description":"The stage. Up to 60 characters; longer is cut."},"proof":{"type":"string","maxLength":240,"description":"What backers will see. Up to 240 characters; longer is cut."},"share":{"description":"Its share of the raise, 0 to 1; all add up to 1.","type":"number"},"days":{"description":"Due this many days after the raise is funded.","type":"number"}},"required":["title"]}},"branches":{"description":"Raise: what the money builds, up to 4 branches of up to 3 goals.","type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":30,"description":"A branch of work. Up to 30 characters; longer is cut."},"goals":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"now":{"type":"string"},"next":{"type":"string"},"cost":{"type":"number"},"stage":{"type":"number"},"shot":{"type":"number"}},"required":["title"]}}},"required":["name","goals"]}},"studio":{"type":"string","maxLength":40,"description":"The studio's name, required. Up to 40 characters; longer is cut."},"site":{"type":"string","description":"Its website."},"base":{"type":"string","maxLength":40,"description":"What it is, like \"Independent studio\". Up to 40 characters; longer is cut."},"note":{"type":"string","maxLength":900,"description":"A note to players. Up to 900 characters; longer is cut."},"sign":{"type":"string","maxLength":40,"description":"How it signs off. Up to 40 characters; longer is cut."},"x":{"description":"X handle or link.","type":"string"},"discord":{"description":"Discord invite.","type":"string"},"youtube":{"description":"YouTube channel.","type":"string"},"postTitle":{"type":"string","maxLength":80,"description":"The first post's title. Up to 80 characters; longer is cut."},"postBody":{"type":"string","maxLength":600,"description":"The first post. Up to 600 characters; longer is cut."}},"required":["kind","title"],"additionalProperties":{}},"AgentNotes":{"type":"object","properties":{"controls":{"type":"string","maxLength":400,"description":"The controls: keys, mouse, touch or gamepad, and what each does. Up to 400 characters; longer is cut."},"goals":{"type":"string","maxLength":400,"description":"What a player is trying to do, and how a run or a round ends. Up to 400 characters; longer is cut."}},"description":"How to play, in the developer's words: shown to agents reading the game's page (a hidden section), never to players."},"FileUpload":{"type":"object","properties":{"url":{"description":"The file's https address (preferred).","type":"string","maxLength":2000,"format":"uri"},"data":{"description":"Or the file itself in base64, up to 3 MB.","type":"string","maxLength":4500000},"filename":{"description":"Its name, when sent as data.","type":"string","maxLength":120}}},"BuildUpload":{"type":"object","properties":{"url":{"description":"The file's https address (preferred).","type":"string","maxLength":2000,"format":"uri"},"data":{"description":"Or the file itself in base64, up to 3 MB.","type":"string","maxLength":4500000},"filename":{"description":"Its name, when sent as data.","type":"string","maxLength":120},"version":{"maxLength":32,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+\\- ]*$","description":"Its version, shown with it in the studio and wherever its builds are listed, once a game's draft names it. A build's version is up to 32 characters: letters, digits, dots, dashes, plus signs and spaces, starting with a letter or a digit (like 1.4.0 or 2.0 beta). Left out, the build is called by its number in its game's builds (1 for its first: \"12\"). 422 bad-version when it breaks the rule.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}}},"MediaUpload":{"type":"object","properties":{"url":{"description":"The file's https address (preferred).","type":"string","maxLength":2000,"format":"uri"},"data":{"description":"Or the file itself in base64, up to 3 MB.","type":"string","maxLength":4500000},"filename":{"description":"Its name, when sent as data.","type":"string","maxLength":120},"game":{"description":"For a picture or video to attach to a game's update (post_update's media): that game's slug. Kept where only its team can upload to; without it, a file can't be attached to an update.","type":"string","maxLength":80}}},"PostBody":{"type":"object","properties":{"title":{"type":"string","minLength":2,"maxLength":80,"description":"Its title, 2 to 80 characters."},"body":{"default":"","description":"Its words, in Markdown, up to 10,000 characters (the marks count): **bold**, *italic*, a heading (a line starting with ##), lists (- or 1.), a quote (>) and links ([words](https://...), or an address written out; http and https only). No HTML (it shows as typed) and no pictures by Markdown: those go in media. 2 characters at least, unless it has media. Its notices and emails get it as plain words, the first 240 characters.","type":"string","maxLength":10000},"media":{"description":"Up to 4 pictures (PNG, JPEG or WebP, 12 MB each), or a video (MP4, WebM or MOV, 80 MB) and up to 3 pictures, in order: the first is the post's picture in the feed, the rest open from it. Each uploaded for this game (upload_media with game); any other address is refused.","maxItems":4,"type":"array","items":{"anyOf":[{"type":"string","maxLength":600},{"type":"object","properties":{"src":{"type":"string","maxLength":600,"description":"Its address, from upload_media with game."},"kind":{"description":"Read from the file; said here or not.","type":"string","enum":["image","video"]},"still":{"description":"A video's first frame, a picture uploaded the same way: shown until it plays.","type":"string","maxLength":600},"w":{"type":"integer"},"h":{"type":"integer"},"copies":{"description":"A picture's smaller copies (the composer makes them as it uploads), uploaded the same way.","type":"object","properties":{"w":{"type":"integer"},"at":{"maxItems":6,"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string","maxLength":600}},"required":["w","src"]}}},"required":["w","at"]}},"required":["src"]}]}},"at":{"description":"To post it later: when it goes up, in milliseconds or as an ISO time with its zone (2026-11-08T18:00:00Z), 5 minutes to 90 days ahead. Until then only its team sees it (listUpdates, its page) and nobody is told; at its time it goes up as if posted then (its date that moment) and its audience is told once. Up to 20 waiting a game. Left out: now.","type":["number","string","null"]}},"required":["title"]},"PostKept":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"body":{"type":"string","description":"Its words, as Markdown."},"md":{"type":"boolean","const":true},"media":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video"]},"src":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"},"still":{"type":"string"},"copies":{"type":"object","properties":{"w":{"type":"integer"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"required":["kind","src"]}},"at":{"type":"number","description":"When it went up (milliseconds since 1970)."},"url":{"type":"string","description":"Where it shows: the game's page, at its Updates."},"told":{"type":"integer","description":"How many players were told in their notices (backers, owners, wishlisters, the studio's followers; none for a private game; none yet for one set to go up later)."},"scheduled":{"description":"When it goes up (milliseconds), for one set to go up later; null once it is up.","type":["number","null"]}},"required":["id","title","body","md","media","at","url","told","scheduled"]},"PostEdit":{"type":"object","properties":{"title":{"type":"string","minLength":2,"maxLength":80,"description":"Its title, 2 to 80 characters."},"body":{"description":"Its words, in Markdown, as for posting (up to 10,000 characters; 2 at least unless it has media). Any post of the game, Player Two's own (a release, a raise's news, a stage, a vote) too: it keeps its kind.","type":"string","maxLength":10000},"media":{"description":"Its attachments in their new order, the whole list (the ones it had that you keep, from listUpdates, and new ones from uploadMedia with game): up to 4 pictures, or a video and 3. [] takes them all out. Left out, they stay. A file taken out leaves the store a day later, unless another of the game's posts shows it.","maxItems":4,"type":"array","items":{"anyOf":[{"type":"string","maxLength":600},{"type":"object","properties":{"src":{"type":"string","maxLength":600,"description":"Its address, from upload_media with game."},"kind":{"description":"Read from the file; said here or not.","type":"string","enum":["image","video"]},"still":{"description":"A video's first frame, a picture uploaded the same way: shown until it plays.","type":"string","maxLength":600},"w":{"type":"integer"},"h":{"type":"integer"},"copies":{"description":"A picture's smaller copies (the composer makes them as it uploads), uploaded the same way.","type":"object","properties":{"w":{"type":"integer"},"at":{"maxItems":6,"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string","maxLength":600}},"required":["w","src"]}}},"required":["w","at"]}},"required":["src"]}]}},"at":{"description":"A post waiting to go up: its new time, as for posting (5 minutes to 90 days ahead). One already up is 409.","type":["number","string"]},"now":{"description":"A post waiting to go up: up now, its audience told now (Post now). One already up is 409.","type":"boolean","const":true},"pinned":{"description":"true pins it first in the game's Updates (any post, Player Two's own \"Out now\" too), unpinning the one pinned before: one a game. false unpins it.","type":"boolean"}}},"PostRead":{"type":"object","properties":{"id":{"type":"string","description":"What editUpdate and deleteUpdate take."},"kind":{"type":"string","enum":["devlog","unlock","vote","release","raise"],"description":"devlog: its team wrote it (edited, pinned, deleted). The rest are Player Two's own about the game: a release, a stage, a vote, a raise's news (its team edits their title, words and attachments and pins them, and they keep their kind; they can't be deleted)."},"title":{"type":"string"},"body":{"type":"string"},"md":{"type":"boolean","description":"Its words are Markdown; otherwise plain words."},"media":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video"]},"src":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"},"still":{"type":"string"},"copies":{"type":"object","properties":{"w":{"type":"integer"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"required":["kind","src"]}},"at":{"type":"number","description":"When it went up (milliseconds since 1970)."},"edited":{"description":"When its team last edited it (milliseconds); its page says Edited.","type":["number","null"]},"pinned":{"description":"When it was pinned first in the game's Updates (milliseconds), or null.","type":["number","null"]},"scheduled":{"description":"When it goes up (milliseconds), while it waits to: only its team sees it, and nobody is told until then. null once it is up.","type":["number","null"]},"editable":{"type":"boolean","description":"Its title, words and attachments may be edited by its team: every post still up, Player Two's own too (since 2026-11-09)."},"deletable":{"type":"boolean","description":"It may be deleted: a devlog, the team's own. Player Two's own posts can't be."},"url":{"type":"string"}},"required":["id","kind","title","body","md","media","at","edited","pinned","scheduled","editable","deletable","url"]},"PostList":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"What editUpdate and deleteUpdate take."},"kind":{"type":"string","enum":["devlog","unlock","vote","release","raise"],"description":"devlog: its team wrote it (edited, pinned, deleted). The rest are Player Two's own about the game: a release, a stage, a vote, a raise's news (its team edits their title, words and attachments and pins them, and they keep their kind; they can't be deleted)."},"title":{"type":"string"},"body":{"type":"string"},"md":{"type":"boolean","description":"Its words are Markdown; otherwise plain words."},"media":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video"]},"src":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"},"still":{"type":"string"},"copies":{"type":"object","properties":{"w":{"type":"integer"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"required":["kind","src"]}},"at":{"type":"number","description":"When it went up (milliseconds since 1970)."},"edited":{"description":"When its team last edited it (milliseconds); its page says Edited.","type":["number","null"]},"pinned":{"description":"When it was pinned first in the game's Updates (milliseconds), or null.","type":["number","null"]},"scheduled":{"description":"When it goes up (milliseconds), while it waits to: only its team sees it, and nobody is told until then. null once it is up.","type":["number","null"]},"editable":{"type":"boolean","description":"Its title, words and attachments may be edited by its team: every post still up, Player Two's own too (since 2026-11-09)."},"deletable":{"type":"boolean","description":"It may be deleted: a devlog, the team's own. Player Two's own posts can't be."},"url":{"type":"string"},"comments":{"type":"integer","description":"How many comments it has (not counting ones taken down)."},"new":{"type":"integer","description":"How many of them are new for the caller: written since they last read the post's comments (markCommentsRead), not by them, not taken down. All of them until they first read it."},"locked":{"type":"boolean","description":"Its comments are locked: nobody can add one."}},"required":["id","kind","title","body","md","media","at","edited","pinned","scheduled","editable","deletable","url","comments","new","locked"]},"description":"Its posts not deleted, up to 100: the ones waiting to go up first (soonest first), then the pinned one, then the newest."}},"required":["posts"]},"PostGone":{"type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"},"notices":{"type":"integer","description":"The notices it had sent, taken out of players' lists."}},"required":["deleted","id","notices"]},"CommentBody":{"type":"object","properties":{"body":{"type":"string","maxLength":4000,"description":"The comment, 1 to 2,000 characters (plain words)."},"reply":{"description":"The id of the comment it replies to, on the same post.","type":["string","null"]}},"required":["body"]},"CommentLock":{"type":"object","properties":{"locked":{"type":"boolean","description":"true: no more comments on this post; false opens them again."}},"required":["locked"]},"CommentList":{"type":"object","properties":{"post":{"type":"string"},"count":{"type":"integer","description":"Its comments not taken down."},"locked":{"type":"boolean"},"keeper":{"type":"boolean","description":"The caller looks after them: the game's team, or an admin (sees ones taken down)."},"can":{"type":"boolean","description":"The caller may comment: signed in, not banned, comments not locked."},"team":{"type":"boolean","description":"The caller is on the game's team (its studio's owner or a member): what they have read is kept, and `new` counts for them."},"new":{"type":"integer","description":"For the game's team: how many of its comments are new for the caller (since they last read them, not their own, not taken down). Reading them doesn't mark them read: markCommentsRead does. 0 for anyone else."},"comments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"body":{"type":"string"},"by":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"},"dev":{"type":"boolean","description":"Its game's team (marked Developer)."},"seed":{"type":"string"},"pic":{"type":"string"}},"required":["handle","name","dev"]},"at":{"type":"number","description":"When it was written (milliseconds since 1970)."},"removed":{"type":"boolean"},"mine":{"type":"boolean"},"new":{"type":"boolean","description":"New for the caller (on the game's team): written since they last read this post's comments, and not by them."},"reply":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},{"type":"null"}],"description":"The comment it replies to, and who wrote that."}},"required":["id","body","by","at","removed","mine","new","reply"]},"description":"Oldest first, the newest last (up to 200)."}},"required":["post","count","locked","keeper","can","team","new","comments"]},"CommentKept":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"number","description":"When it was written (milliseconds since 1970)."},"reply":{"type":["string","null"]}},"required":["id","at","reply"]},"CommentGone":{"type":"object","properties":{"id":{"type":"string"},"removed":{"type":"boolean","const":true}},"required":["id","removed"]},"CommentsLocked":{"type":"object","properties":{"post":{"type":"string"},"locked":{"type":"boolean"}},"required":["post","locked"]},"CommentsRead":{"type":"object","properties":{"post":{"description":"The post whose comments were read (its id, from the game's posts). Left out: every post of the game (Mark all read).","type":"string","maxLength":40},"until":{"description":"Read up to this moment (milliseconds), as the comment last read was written; the newer ones stay new. Left out: up to now.","type":"number"}}},"CommentsReadDone":{"type":"object","properties":{"read":{"type":"integer","description":"How many of the game's posts were marked read."},"new":{"type":"integer","description":"The comments on the game's posts still new for the caller."}},"required":["read","new"]},"DraftBody":{"type":"object","properties":{"draft":{"$ref":"#/components/schemas/Draft"}},"required":["draft"]},"SubmitBody":{"type":"object","properties":{"draft":{"$ref":"#/components/schemas/Draft"},"publish":{"default":true,"description":"false keeps it as a draft in the studio; default true.","type":"boolean"},"release":{"description":"How it is listed: out now (the default), kept private, or coming on a day.","type":"object","properties":{"stand":{"description":"out: listed and playable now (the default). private: its page and its play only for its team, its testers and Player Two's admins, and in no list. date: in the lists with its day and Wishlist in place of Play, played only by its team and testers until then; then out by itself, its wishlisters told. A store game only (a raise opens on its own day); once out, it stays out.","type":"string","enum":["out","private","date"]},"at":{"description":"With date: when it comes out, in milliseconds or as an ISO time, up to 365 days ahead (a time already past is now). A game with its day set: the day moved.","type":["number","string"]}}}},"required":["draft"]},"ReleaseBody":{"type":"object","properties":{"stand":{"description":"out: listed and playable now (the default). private: its page and its play only for its team, its testers and Player Two's admins, and in no list. date: in the lists with its day and Wishlist in place of Play, played only by its team and testers until then; then out by itself, its wishlisters told. A store game only (a raise opens on its own day); once out, it stays out.","type":"string","enum":["out","private","date"]},"at":{"description":"With date: when it comes out, in milliseconds or as an ISO time, up to 365 days ahead (a time already past is now). A game with its day set: the day moved.","type":["number","string"]},"countdown":{"description":"Its page counts down to its day: on unless turned off.","type":"boolean"},"asks":{"description":"Players may ask to playtest it from its page while it isn't out (its team says yes or no to each): on unless turned off.","type":"boolean"}}},"ReleaseNow":{"type":"object","properties":{"stand":{"type":"string","enum":["out","private","date"]},"at":{"description":"When it comes out (milliseconds), while it has a day ahead or since it came out on one.","type":["number","null"]},"countdown":{"type":"boolean"},"asks":{"type":"boolean"}},"required":["stand","at","countdown","asks"]},"TesterAct":{"type":"object","properties":{"do":{"type":"string","enum":["invite","yes","no","drop"],"description":"invite: a tester at once, told so. yes or no: to a player who asked from the game's page. drop: off the game (a tester or an ask)."},"handle":{"type":"string","minLength":1,"maxLength":41,"description":"The player's handle (with or without @)."}},"required":["do","handle"]},"TesterList":{"type":"object","properties":{"stand":{"type":"string","enum":["out","private","date"]},"at":{"description":"When it comes out (milliseconds), while it has a day ahead or since it came out on one.","type":["number","null"]},"countdown":{"type":"boolean"},"asks":{"type":"boolean"},"link":{"$ref":"#/components/schemas/PlaytestLink"},"asked":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"},"level":{"type":"integer"},"since":{"type":"number","description":"When they joined Player Two (milliseconds since 1970)."},"state":{"type":"string","enum":["asked","in","declined"]},"note":{"type":"string","description":"What they wrote with their ask."},"invited":{"type":"boolean"},"link":{"type":"boolean","description":"They came by the game's playtest link: taken off again when the link is turned off or reset, or the game comes out."},"at":{"type":"number","description":"When they asked, were invited or came by the link (milliseconds since 1970)."},"decidedAt":{"type":["number","null"]},"history":{"description":"For an ask waiting for an answer: what they have done on Player Two, to decide by.","type":"object","properties":{"hours":{"type":"integer"},"games":{"type":"integer","description":"How many games they have played (tests left out)."},"trophies":{"type":"integer"},"reviews":{"type":"integer"},"latest":{"type":"array","items":{"type":"object","properties":{"game":{"type":"string"},"title":{"type":"string"},"stars":{"type":"integer"},"body":{"type":"string"},"at":{"type":"number"}},"required":["game","title","stars","body","at"]}}},"required":["hours","games","trophies","reviews","latest"]}},"required":["handle","name","level","since","state","note","invited","link","at","decidedAt"],"additionalProperties":{}}},"testers":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"},"level":{"type":"integer"},"since":{"type":"number","description":"When they joined Player Two (milliseconds since 1970)."},"state":{"type":"string","enum":["asked","in","declined"]},"note":{"type":"string","description":"What they wrote with their ask."},"invited":{"type":"boolean"},"link":{"type":"boolean","description":"They came by the game's playtest link: taken off again when the link is turned off or reset, or the game comes out."},"at":{"type":"number","description":"When they asked, were invited or came by the link (milliseconds since 1970)."},"decidedAt":{"type":["number","null"]},"history":{"description":"For an ask waiting for an answer: what they have done on Player Two, to decide by.","type":"object","properties":{"hours":{"type":"integer"},"games":{"type":"integer","description":"How many games they have played (tests left out)."},"trophies":{"type":"integer"},"reviews":{"type":"integer"},"latest":{"type":"array","items":{"type":"object","properties":{"game":{"type":"string"},"title":{"type":"string"},"stars":{"type":"integer"},"body":{"type":"string"},"at":{"type":"number"}},"required":["game","title","stars","body","at"]}}},"required":["hours","games","trophies","reviews","latest"]}},"required":["handle","name","level","since","state","note","invited","link","at","decidedAt"],"additionalProperties":{}}},"declined":{"type":"integer"}},"required":["stand","at","countdown","asks","link","asked","testers","declined"]},"TesterDone":{"type":"object","properties":{"handle":{"type":"string"},"state":{"anyOf":[{"type":"string","enum":["in","declined"]},{"type":"null"}]}},"required":["handle","state"]},"PlaytestLinkBody":{"type":"object","properties":{"on":{"description":"true: anyone with the link plays it as a tester until it is out (signed out, they see its page and sign in to play). false: off, and every tester who came by it is taken off; invited testers stay. Turned on again, it is the same link. A game not out yet only: kept private, or coming on a day.","type":"boolean"},"reset":{"description":"true: a new link. The old one, and every browser holding it, stops at once, and every tester who came by it is taken off. It stays on or off as it was.","type":"boolean"}}},"PlaytestLink":{"type":"object","properties":{"on":{"type":"boolean"},"url":{"description":"The link, while it is on: the game's page with its secret after a # (never sent to a server by a browser, so in no log or referrer). Share it as it is. Null while it is off, or once the game is out.","type":["string","null"]},"since":{"description":"When it was turned on (milliseconds).","type":["number","null"]}},"required":["on","url","since"]},"BoardInput":{"type":"object","properties":{"key":{"type":"string","maxLength":60,"description":"a-z, 0-9 and dashes, up to 40: what the game's code fills it by."},"name":{"type":"string","maxLength":40,"description":"Up to 40 characters."},"order":{"description":"Which way is better.","type":"string","enum":["high","low"]},"format":{"description":"How a score reads: a number, a time in milliseconds, or points.","type":"string","enum":["number","time","points"]},"min":{"description":"The lowest score it takes.","type":["number","null"]},"max":{"description":"The highest score it takes.","type":["number","null"]},"cooldown":{"minimum":0,"maximum":86400,"description":"Seconds a player waits between scores.","type":"number"},"trusted":{"description":"Only the game's own server sends its scores.","type":"boolean"}},"required":["key","name"]},"TrophyInput":{"type":"object","properties":{"key":{"type":"string","maxLength":60,"description":"a-z, 0-9 and dashes, up to 40: what the game's code earns it by. \"complete\" is the completion trophy."},"name":{"default":"","maxLength":40,"description":"Up to 40 characters.","type":"string"},"how":{"maxLength":120,"description":"How to earn it, up to 120 characters.","type":"string"},"tier":{"default":0,"minimum":1,"maximum":4,"description":"1 to 4, worth 10, 25, 50 and 100 XP; a game's trophies add up to 1,000 XP at most.","type":"integer"},"hidden":{"description":"Show only that there is one until it is earned.","type":"boolean"},"art":{"description":"A picture from upload_media.","type":"string","maxLength":600},"sign":{"description":"The small picture its relic (a sculpture of cubes) is built from: rows of # (a cube) and . (none) from the top, up to 16 by 16, as one string separated by / or a list of rows; or a library sign's name (crown, rocket, gem, pad, bell, lantern, flag, fist, star, bolt, key, sword, shield, heart, moon, flame, hourglass, anchor, tower, joystick, gift). Left out, it keeps its sign; empty, Player Two picks one by its key.","anyOf":[{"anyOf":[{"type":"string","maxLength":600},{"maxItems":40,"type":"array","items":{"type":"string","maxLength":64}}]},{"type":"null"}]},"stat":{"description":"Earned by itself when this stat reaches target.","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"target":{"description":"A whole number, 1 or more.","type":["number","null"]},"serverOnly":{"description":"Only the game's own server can give it, with a game key.","type":"boolean"}},"required":["key"]},"PushManifest":{"type":"object","properties":{"entry":{"description":"The page that starts the game; index.html by default.","type":"string"},"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Inside the folder, forward slashes."},"size":{"type":"integer","minimum":0,"description":"Bytes, up to 512 MB a file."},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"type":{"description":"Its media type, if the tool knows it.","type":"string"}},"required":["path","size","sha256"]},"description":"Up to 20,000 files and 5 GB."},"note":{"maxLength":1000,"description":"What changed, in the developer's words: shown with the build in the studio's list (p2 push --note).","type":["string","null"]},"post":{"description":"Also put the note on the game's Updates as a release post once the build is live (p2 push --post); needs a note.","type":"boolean"},"testers":{"description":"To the game's testers first (p2 push --testers): its players keep the live build, and its team, testers and Player Two's admins play this one, as a test, until it is made live from the studio's list of builds. A newer one takes its place. For a game that is out with a build of its own; otherwise the build goes live as usual.","type":"boolean"},"version":{"maxLength":32,"pattern":"^[A-Za-z0-9][A-Za-z0-9.+\\- ]*$","description":"Its version (p2 push --version 1.4.0), shown with it in the studio's list, its testers' notice and the agent API. The same files pushed again with another version renames it. Not with room. A build's version is up to 32 characters: letters, digits, dots, dashes, plus signs and spaces, starting with a letter or a digit (like 1.4.0 or 2.0 beta). Left out, the build is called by its number in its game's builds (1 for its first: \"12\"). 422 bad-version when it breaks the rule.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"room":{"description":"Room code alone (p2 push --room), for a game on its own site that Player Two serves: the files are room.js, or a room/ folder with room/room.js and what it imports, and nothing else (no entry). Taken out and never served; the game's rooms opened from then on run it, the rooms open on the code before play on. 409 not-followed for a game Player Two keeps a build of (its room.js goes in the build).","type":"boolean"}},"required":["files"]},"MediaKept":{"type":"object","properties":{"url":{"type":"string","description":"Its address, for the draft."},"type":{"type":"string"},"size":{"type":"integer"},"use":{"type":"string","description":"Where in the draft it goes."},"font":{"type":"object","properties":{"family":{"type":"string"},"weight":{"type":"number"},"italic":{"type":"boolean"}},"required":["family","weight","italic"]}},"required":["url","type","size","use"]},"BuildKept":{"type":"object","properties":{"build":{"type":"string","description":"The address to put in the draft's build."},"files":{"type":"integer"},"version":{"description":"The version it was given (upload_web_build's version), or null: it is called by its number once a game's draft names it.","type":["string","null"]},"kind":{"type":"string","enum":["zip","html"]}},"required":["build","version","kind"]},"DraftChecked":{"type":"object","properties":{"ok":{"type":"boolean","description":"Nothing is missing."},"missing":{"type":"array","items":{"type":"string"},"description":"What still has to be filled in, in the form's order."},"draft":{"description":"The draft as it would be kept.","$ref":"#/components/schemas/DraftKept"},"font":{"description":"The font asked for, as it will be kept.","type":"string"}},"required":["ok","missing","draft"]},"GameSubmitted":{"type":"object","properties":{"slug":{"type":"string"},"url":{"type":"string"},"studio_url":{"type":"string"},"kind":{"type":"string","enum":["store","raise"]},"live":{"type":"boolean"},"identity":{"type":"object","properties":{"needed_for":{"type":"string"},"state":{"type":"string"},"by":{"type":"string"},"verify_at":{"type":"string"}},"required":["needed_for","state","by","verify_at"]},"note":{"type":"string"},"note_draft":{"type":"string"}},"required":["slug","url","studio_url","kind","live"]},"GameEdited":{"type":"object","properties":{"slug":{"type":"string"},"url":{"type":"string"}},"required":["slug","url"]},"OwnGame":{"type":"object","properties":{"slug":{"type":"string"},"url":{"type":"string"},"archived":{"type":"boolean"},"release":{"description":"How it stands before it is out.","$ref":"#/components/schemas/ReleaseNow"},"draft":{"$ref":"#/components/schemas/DraftKept"},"raise":{"anyOf":[{"type":"object","properties":{"raised":{"type":"number"},"backers":{"type":"number"},"goal":{"type":"number"},"opensAt":{"type":"number"},"closesAt":{"type":"number"},"state":{"type":"string"},"history":{"type":"array","items":{"type":"number"}},"fundedAt":{"type":["number","null"]}},"required":["raised","backers","goal","opensAt","closesAt","state","history","fundedAt"],"additionalProperties":{}},{"type":"null"}]},"price":{"type":"number","description":"Its price as its developer set it."},"sale":{"anyOf":[{"type":"object","properties":{"percent":{"type":"number"},"was":{"type":"number","description":"The price before the sale."},"ends":{"type":"number"}},"required":["percent","was","ends"],"additionalProperties":{}},{"type":"null"}]}},"required":["slug","url","archived","release","raise","price","sale"]},"AgentStudio":{"type":"object","properties":{"player":{"type":"string"},"studio":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"handle":{"type":"string"},"url":{"type":"string"}},"required":["name","handle","url"]},{"type":"null"}]},"games":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"kind":{"type":"string"},"url":{"type":"string"},"state":{"type":"string","enum":["live","archived (a draft)","taken down"]},"raise":{"type":"object","properties":{"state":{"type":"string"},"raised":{"type":"number"},"goal":{"type":"number"},"closes_at":{"type":"string"}},"required":["state","raised","goal","closes_at"]}},"required":["slug","title","kind","url","state"]}},"identity":{"type":"object","properties":{"needed_for":{"type":"string"},"state":{"type":"string"},"by":{"type":"string"},"verify_at":{"type":"string"}},"required":["needed_for","state","by","verify_at"]}},"required":["player","studio","games","identity"]},"TrophyDefs":{"type":"object","properties":{"slug":{"type":"string"},"trophies":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"how":{"type":"string"},"tier":{"type":"integer"},"xp":{"type":"integer"},"hidden":{"type":"boolean"},"art":{"type":"string"},"sign":{"type":"string"},"drawn":{"type":"boolean"},"stat":{"type":["string","null"]},"target":{"type":["number","null"]},"serverOnly":{"type":"boolean"},"retired":{"type":"boolean"},"earnedBy":{"type":"integer"},"share":{"type":"integer"},"rarity":{"type":"string","enum":["New","Common","Rare","Epic","Legendary","Mythic","Divine"]},"complete":{"type":"boolean"}},"required":["key","name","how","tier","xp","hidden","art","sign","drawn","stat","target","serverOnly","retired","earnedBy","share","rarity","complete"]}},"budget":{"type":"object","properties":{"used":{"type":"integer"},"left":{"type":"integer"},"max":{"type":"integer"}},"required":["used","left","max"]},"count":{"type":"integer"},"most":{"type":"integer"},"players":{"type":"integer"},"counts":{"type":"string","enum":["yes","waiting","off"],"description":"Whether the game's trophies count toward players' levels yet."}},"required":["slug","trophies","budget","count","most","players","counts"]},"BoardDefs":{"type":"object","properties":{"slug":{"type":"string"},"boards":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"order":{"type":"string","enum":["high","low"]},"format":{"type":"string","enum":["number","time","points"]},"min":{"type":["number","null"]},"max":{"type":["number","null"]},"cooldown":{"type":"integer"},"trusted":{"type":"boolean"},"retired":{"type":"boolean"},"players":{"type":"integer"}},"required":["key","name","order","format","min","max","cooldown","trusted","retired","players"]}},"count":{"type":"integer"},"most":{"type":"integer"}},"required":["slug","boards","count","most"]},"AgentRooms":{"type":"object","properties":{"on":{"type":"boolean","description":"Its multiplayer is Player Two's rooms (stopped says whether Player Two has stopped them)."},"multiplayer":{"type":"string","enum":["rooms","none","own"]},"stopped":{"type":"boolean","description":"Player Two stopped its rooms."},"hosted":{"type":"boolean"},"script":{"anyOf":[{"type":"object","properties":{"build":{"type":"string"},"at":{"type":"number"}},"required":["build","at"]},{"type":"null"}],"description":"The live build's room code (room.js or a room/ folder), run on Player Two's servers."},"now":{"anyOf":[{"type":"object","properties":{"rooms":{"type":"integer"},"players":{"type":"integer"},"builds":{"type":"array","items":{"type":"object","properties":{"build":{"type":"string"},"rooms":{"type":"integer"},"players":{"type":"integer"}},"required":["build","rooms","players"],"additionalProperties":{}},"description":"Its rooms and players by build: a release's rooms play on beside the new build's."},"net":{"anyOf":[{"$ref":"#/components/schemas/RoomsNet"},{"type":"null"}]},"top":{"type":"array","items":{"$ref":"#/components/schemas/RoomNet"}}},"required":["rooms","players","builds","net","top"],"additionalProperties":{}},{"type":"null"}],"description":"Its rooms open now; null when the rooms service didn't answer in time."},"error":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"},"at":{"type":"number"}},"required":["message","at"],"additionalProperties":{}},{"type":"null"}],"description":"The last error its room code threw, or why the live build's couldn't be used."},"slug":{"type":"string"},"units":{"type":"string","description":"What each figure is in, and over what time."}},"required":["on","multiplayer","stopped","hosted","script","now","error","slug","units"],"additionalProperties":{},"description":"A game's rooms now and how their connections are: the studio's Rooms panel, with the eight busiest rooms."},"RoomsView":{"type":"object","properties":{"on":{"type":"boolean","description":"Its multiplayer is Player Two's rooms (stopped says whether Player Two has stopped them)."},"multiplayer":{"type":"string","enum":["rooms","none","own"]},"stopped":{"type":"boolean","description":"Player Two stopped its rooms."},"hosted":{"type":"boolean"},"script":{"anyOf":[{"type":"object","properties":{"build":{"type":"string"},"at":{"type":"number"}},"required":["build","at"]},{"type":"null"}],"description":"The live build's room code (room.js or a room/ folder), run on Player Two's servers."},"now":{"anyOf":[{"type":"object","properties":{"rooms":{"type":"integer"},"players":{"type":"integer"},"builds":{"type":"array","items":{"type":"object","properties":{"build":{"type":"string"},"rooms":{"type":"integer"},"players":{"type":"integer"}},"required":["build","rooms","players"],"additionalProperties":{}},"description":"Its rooms and players by build: a release's rooms play on beside the new build's."},"net":{"anyOf":[{"$ref":"#/components/schemas/RoomsNet"},{"type":"null"}]},"top":{"type":"array","items":{"$ref":"#/components/schemas/RoomNet"}}},"required":["rooms","players","builds","net","top"],"additionalProperties":{}},{"type":"null"}],"description":"Its rooms open now; null when the rooms service didn't answer in time."},"error":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"},"at":{"type":"number"}},"required":["message","at"],"additionalProperties":{}},{"type":"null"}],"description":"The last error its room code threw, or why the live build's couldn't be used."}},"required":["on","multiplayer","stopped","hosted","script","now","error"],"additionalProperties":{}},"RoomsNet":{"type":"object","properties":{"rooms":{"type":"integer","description":"Rooms whose pages reported in the last two minutes."},"ping":{"description":"ms there and back: a room's middle page's; overall, the rooms' weighed by their players.","type":["number","null"]},"p90":{"description":"ms there and back that nine pages in ten are at or under.","type":["number","null"]},"jitter":{"description":"ms: how much the round trip varies (the middle page's).","type":["number","null"]},"down":{"description":"Bytes a second a page, from the room.","type":["number","null"]},"up":{"description":"Bytes a second a page, to the room.","type":["number","null"]},"fixes":{"description":"Corrections a minute a page (a move put right by the room's code).","type":["number","null"]},"rewinds":{"description":"Inputs played again a minute a page (an input-run room).","type":["number","null"]},"drops":{"type":"number","description":"Connections dropped in the last minute (each came back or its seat was let go)."}},"required":["rooms","ping","p90","jitter","down","up","fixes","rewinds","drops"],"additionalProperties":{},"description":"Every room's connections, each room's figures weighed by its players."},"RoomNet":{"type":"object","properties":{"mode":{"type":"string"},"players":{"type":"integer"},"watchers":{"type":"integer"},"build":{"type":"string"},"at":{"type":"number","description":"When the room said these (ms since 1970): every 30 s at most."},"pages":{"type":"integer","description":"How many of its pages reported."},"ping":{"description":"ms there and back: a room's middle page's; overall, the rooms' weighed by their players.","type":["number","null"]},"p90":{"description":"ms there and back that nine pages in ten are at or under.","type":["number","null"]},"jitter":{"description":"ms: how much the round trip varies (the middle page's).","type":["number","null"]},"down":{"description":"Bytes a second a page, from the room.","type":["number","null"]},"up":{"description":"Bytes a second a page, to the room.","type":["number","null"]},"fixes":{"description":"Corrections a minute a page (a move put right by the room's code).","type":["number","null"]},"rewinds":{"description":"Inputs played again a minute a page (an input-run room).","type":["number","null"]},"drops":{"type":"number","description":"Connections dropped in the last minute (each came back or its seat was let go)."}},"required":["mode","players","watchers","build","at","pages","ping","p90","jitter","down","up","fixes","rewinds","drops"],"additionalProperties":{},"description":"One of the busiest rooms (most players first)."},"PushStarted":{"type":"object","properties":{"build":{"type":"string","description":"The build's key, for the last step."},"upload":{"type":"array","items":{"type":"object","properties":{"sha256":{"type":"string"},"method":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}}},"required":["sha256","method","url","headers"]},"description":"Where to send each file the store doesn't hold yet."}},"required":["build","upload"]},"PushDone":{"type":"object","properties":{"live":{"type":"boolean","description":"true: its players get it from their next page load (room code alone: the game's rooms opened from now run it). false: it went to its testers first (testers)."},"url":{"type":"string","description":"https://<slug>.playertwo.cc/ (room code alone: the game's page)"},"room":{"description":"It was room code alone (the manifest's room).","type":"boolean","const":true},"build":{"description":"Room code alone: its key, which the game's rooms name.","type":"string"},"sha":{"description":"Room code alone: its SHA-256, as the rooms check it.","type":"string"},"testers":{"description":"It is the game's testers' build: its players keep the live one.","type":"boolean","const":true},"version":{"description":"The build's version: the manifest's, or its number in the game's builds (\"12\"). Not for room code alone.","type":["string","null"]},"posted":{"description":"The note went on the game's Updates (the manifest's post).","type":"boolean","const":true}},"required":["live","url"]},"BuildPick":{"type":"object","properties":{"key":{"type":"string","maxLength":40,"description":"The build's key, from the game's list of builds."},"note":{"description":"Why, kept with the change in the game's history of builds.","type":"string","maxLength":1000}},"required":["key"]},"BuildName":{"type":"object","properties":{"key":{"type":"string","maxLength":40,"description":"The build's key, from the game's list of builds."},"version":{"type":"string","maxLength":32,"description":"Its version. A build's version is up to 32 characters: letters, digits, dots, dashes, plus signs and spaces, starting with a letter or a digit (like 1.4.0 or 2.0 beta). 422 bad-version otherwise."}},"required":["key","version"]},"BuildNamed":{"type":"object","properties":{"key":{"type":"string"},"version":{"type":"string"}},"required":["key","version"]},"BuildHistory":{"type":"object","properties":{"builds":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"version":{"description":"Its version: what its developer called it, or its number in the game's builds (\"12\"); null only for a build kept the older way that never had one.","type":["string","null"]},"live":{"type":"boolean","description":"The game's build now."},"testers":{"type":"boolean","description":"The build out to its testers first (and its team), not yet its players'."},"testersAt":{"description":"When it last went to its testers (milliseconds).","type":["number","null"]},"testing":{"type":"integer","description":"Its team and testers on it now."},"source":{"type":"string","enum":["push","zip","page","github","legacy"]},"ref":{"description":"Its commit, or what the push named it.","type":["string","null"]},"note":{"type":["string","null"]},"files":{"type":"integer"},"size":{"type":"integer","description":"Bytes."},"at":{"type":"number","description":"When it was sent (milliseconds since 1970)."},"liveAt":{"description":"When it was first live (milliseconds).","type":["number","null"]},"retiredAt":{"description":"When another took its place (milliseconds).","type":["number","null"]},"by":{"anyOf":[{"type":"object","properties":{"handle":{"type":"string"},"key":{"description":"The name of the agent key that did it, if one did.","type":["string","null"]}},"required":["handle","key"]},{"type":"null"}],"description":"Who did it: the player, and their agent key if one; null for a build from before senders were kept."},"diff":{"anyOf":[{"type":"object","properties":{"from":{"type":"string"},"added":{"type":"integer"},"changed":{"type":"integer"},"removed":{"type":"integer"}},"required":["from","added","changed","removed"]},{"type":"null"}],"description":"Its files against the build before it."},"players":{"type":"integer","description":"Players on it now (the game's team left out)."},"try":{"type":"string","description":"Where to play it in a tab of its own without making it live, for two hours; empty if it can't be."},"last":{"anyOf":[{"type":"object","properties":{"at":{"type":"number","description":"When (milliseconds since 1970)."},"why":{"type":"string","enum":["push","upload","github","rollback","own-site","testers"],"description":"push, upload or github: a new build; rollback: one played before made live again; testers: the testers' build made live; own-site: the game moved to its own site."},"by":{"anyOf":[{"type":"object","properties":{"handle":{"type":"string"},"key":{"description":"The name of the agent key that did it, if one did.","type":["string","null"]}},"required":["handle","key"]},{"type":"null"}],"description":"Who did it: the player, and their agent key if one; null for a build from before senders were kept."},"note":{"type":["string","null"]}},"required":["at","why","by","note"]},{"type":"null"}],"description":"The latest time it became the game's build."}},"required":["key","version","live","testers","testersAt","testing","source","ref","note","files","size","at","liveAt","retiredAt","by","diff","players","try","last"]}},"switches":{"type":"array","items":{"type":"object","properties":{"at":{"type":"number","description":"When (milliseconds since 1970)."},"from":{"type":["string","null"]},"to":{"type":["string","null"]},"by":{"anyOf":[{"type":"object","properties":{"handle":{"type":"string"},"key":{"description":"The name of the agent key that did it, if one did.","type":["string","null"]}},"required":["handle","key"]},{"type":"null"}],"description":"Who did it: the player, and their agent key if one; null for a build from before senders were kept."},"note":{"type":["string","null"]},"why":{"type":"string","enum":["push","upload","github","rollback","own-site","testers"],"description":"push, upload or github: a new build; rollback: one played before made live again; testers: the testers' build made live; own-site: the game moved to its own site."}},"required":["at","from","to","by","note","why"]},"description":"The game's latest changes of build, newest first (30 at most)."},"keep":{"type":"object","properties":{"days":{"type":"integer"},"builds":{"type":"integer"}},"required":["days","builds"],"description":"How long a build is kept after another takes its place, and how many are always kept."},"testersFirst":{"type":"boolean","description":"Whether a new build can go to its testers first: a game that is out, with a build live."}},"required":["builds","switches","keep","testersFirst"]},"BuildLive":{"type":"object","properties":{"live":{"type":"boolean","const":true},"key":{"type":"string","description":"The game's build now: players get it on their next page load."},"posted":{"description":"The testers' build made live: its note went on the game's Updates (its push asked for that).","type":"boolean","const":true}},"required":["live","key"]},"Guide":{"type":"object","properties":{"what":{"type":"string"},"auth":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}},"required":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"fields":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Every field of the draft, its limits and its choices."}},"required":["what","auth","steps","required","fields"],"additionalProperties":{},"description":"How to submit a game: every field, its limits, the rules, the steps, and an example of each kind."},"SessionStart":{"type":"object","properties":{"from":{"type":"string","enum":["web","desktop"]},"device":{"description":"The app's own key for this computer (desktop only).","type":"object","properties":{"key":{"type":"string","minLength":32,"maxLength":128},"name":{"type":"string","maxLength":80},"platform":{"type":"string","enum":["windows","mac","linux"]}},"required":["key","name","platform"]},"build":{"description":"The download the app installed (desktop only).","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["from"]},"SessionOpened":{"type":"object","properties":{"token":{"type":"string","description":"The play session's token (p2s_...), the game's Bearer for /api/v1/sdk."},"session":{"type":"string"},"kind":{"type":"string","enum":["full","demo","test"]},"ticket":{"description":"A desktop session's launch ticket.","type":["string","null"]},"ticketExp":{"type":["number","null"]},"player":{"$ref":"#/components/schemas/GamePlayer"},"game":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},"origins":{"description":"A web session: the origins the player's page may hand the session to.","type":"array","items":{"type":"string"}}},"required":["token","session","kind","ticket","ticketExp","player","game"]},"Beat":{"type":"object","properties":{"status":{"description":"The line the player's friends see (up to 80 characters kept); null clears it.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"join":{"description":"What friends join the player with (invite params); null clears it."}}},"GameEvent":{"type":"object","properties":{"event":{"type":"string","enum":["gameplay.start","gameplay.stop","loading.done"]}},"required":["event"]},"Invite":{"type":"object","properties":{"params":{"description":"What whoever follows the link is given as p2.params."}}},"ScoreSend":{"type":"object","properties":{"value":{"type":"integer","description":"A whole number (a time in milliseconds)."}},"required":["value"]},"StatsSend":{"type":"object","properties":{"set":{"description":"Stats to set: { key: whole number }.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"add":{"description":"Stats to add to: { key: amount }.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"SaveSend":{"type":"object","properties":{"data":{"type":"string","description":"The save, base64, up to 1 MB."},"version":{"description":"The version the game read: a save made elsewhere since is refused (409 version).","anyOf":[{"type":"integer"},{"type":"null"}]}},"required":["data"]},"MirrorSend":{"type":"object","properties":{"data":{"type":"string","description":"JSON text: { items: { key: value } } for local, { databases: [...] } for idb; up to 2 MB."},"version":{"anyOf":[{"type":"integer"},{"type":"null"}]},"skipped":{"description":"What was too big to copy.","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"size":{"type":"number"}},"required":["key","size"]}}},"required":["data"]},"CloudSend":{"anyOf":[{"type":"object","properties":{"done":{"type":"string","maxLength":300,"description":"The key the upload went to."},"size":{"type":"integer"}},"required":["done","size"]},{"type":"object","properties":{"size":{"type":"integer","description":"Bytes, up to 100 MB."}},"required":["size"]}]},"AirCall":{"oneOf":[{"type":"object","properties":{"do":{"type":"string","const":"start"},"sdp":{"type":"string","minLength":10,"maxLength":20000,"description":"The SDK's WebRTC offer: one sending video track."},"mid":{"type":"string","maxLength":16,"description":"That track's mid in the offer."}},"required":["do","sdp","mid"]},{"type":"object","properties":{"do":{"type":"string","const":"up","description":"The connection to Realtime is up and the picture flowing: listed for the Plaza from now."}},"required":["do"]},{"type":"object","properties":{"do":{"type":"string","const":"check","description":"Whether the picture still reaches Realtime (410 gone when it doesn't: send it again)."},"key":{"description":"The picture meant (start's key): one since replaced is gone, and the new one is left be.","type":"string","maxLength":40}},"required":["do"]},{"type":"object","properties":{"do":{"type":"string","const":"stop","description":"Off air."}},"required":["do"]}]},"SeatAsk":{"type":"object","properties":{"mode":{"description":"A match of this mode (letters, numbers, - and _): the fullest open room of it and its size, or a new one. \"main\" when none is said.","type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,31}$"},"size":{"description":"The match's seats (8 when none is said, up to the game's limit).","type":"integer","minimum":1,"maximum":256},"code":{"description":"A private room's six-letter code, to join it.","type":"string","maxLength":12},"create":{"description":"A new private room: its code comes back in the room.","type":"boolean"},"party":{"description":"The player's party's room, for its members only (409 no-party otherwise).","type":"boolean"},"room":{"description":"The room the player was in (to come back to it with resume), or the one to watch.","type":"string","maxLength":200},"resume":{"description":"The key the room gave the player, to come back to the same seat.","type":"string","pattern":"^[A-Za-z0-9_-]{16,48}$"},"build":{"description":"The build the player's page loaded (__P2_HOSTED__.build): a room is for one build, and a build no longer kept is refused (409 old-build).","type":"string","pattern":"^[a-f0-9]{24}$"},"world":{"description":"A persistent world, and its area below: the live build only.","type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,31}$"},"area":{"description":"The world's area (\"main\" when none is said).","type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,31}$"},"skill":{"description":"Match by skill: rooms of players rated near this one first (room.result in the game's room code).","type":"boolean"},"watch":{"description":"Watch the room named in room: no seat, sees everything, says nothing.","type":"boolean"},"record":{"description":"Keep the room as a replay (asked by its first player).","type":"boolean"},"teams":{"description":"How many teams the room has (2 to 16), its size split evenly between them (422 bad-teams otherwise): each player's team is in room.players. Part of what a match is matched by, with mode and size.","type":"integer","minimum":2,"maximum":16},"together":{"description":"A match for the player's whole party: its members seated in one room, on one team, their seats held 30 seconds while each asks; matched by skill on the party's mean rating with skill. 409 no-party, party-too-big (more than a team holds).","type":"boolean"}},"description":"What seat the player wants: a match (mode, size, teams, skill, together), a private room (create, or code), the party's room, a world's area, a room to come back to (room, resume) or join by its id (room), or to watch. Each field is optional."},"ReplayAsk":{"type":"object","properties":{"id":{"type":"string","maxLength":64,"description":"A replay's id, from p2.rooms.replays."}},"required":["id"]},"RoomsLive":{"type":"object","properties":{"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"mode":{"type":"string"},"size":{"type":"number"},"players":{"type":"number"}},"required":["id","mode","size","players"],"additionalProperties":{}},"description":"The game's public rooms open now, fullest first, up to 20: { id, mode, size, players, watchers, skill, closed, build }."}},"required":["rooms"]},"Replays":{"type":"object","properties":{"replays":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":{}},"description":"The game's replays, newest first: { id, room, mode, size, players, from, ms, bytes }."}},"required":["replays"]},"ServiceAct":{"oneOf":[{"type":"object","properties":{"do":{"type":"string","const":"trophy.save"},"trophy":{"type":"object","properties":{"key":{"type":"string","maxLength":60,"description":"a-z, 0-9 and dashes, up to 40: what the game's code earns it by. \"complete\" is the completion trophy."},"name":{"type":"string","maxLength":200},"how":{"maxLength":120,"description":"How to earn it, up to 120 characters.","type":"string"},"tier":{"type":"integer"},"hidden":{"description":"Show only that there is one until it is earned.","type":"boolean"},"art":{"description":"A picture from upload_media.","type":"string","maxLength":600},"sign":{"anyOf":[{"anyOf":[{"type":"string","maxLength":600},{"maxItems":40,"type":"array","items":{"type":"string","maxLength":64}}]},{"type":"null"}]},"stat":{"description":"Earned by itself when this stat reaches target.","anyOf":[{"type":"string","maxLength":60},{"type":"null"}]},"target":{"description":"A whole number, 1 or more.","type":["number","null"]},"serverOnly":{"description":"Only the game's own server can give it, with a game key.","type":"boolean"}},"required":["key","name","tier"]}},"required":["do","trophy"]},{"type":"object","properties":{"do":{"type":"string","const":"trophy.sign"},"key":{"type":"string","maxLength":60},"sign":{"anyOf":[{"anyOf":[{"type":"string","maxLength":600},{"maxItems":40,"type":"array","items":{"type":"string","maxLength":64}}]},{"type":"null"}]}},"required":["do","key","sign"]},{"type":"object","properties":{"do":{"type":"string","const":"trophy.retire"},"key":{"type":"string","maxLength":60},"retired":{"type":"boolean"}},"required":["do","key","retired"]},{"type":"object","properties":{"do":{"type":"string","const":"trophy.delete"},"key":{"type":"string","maxLength":60}},"required":["do","key"]},{"type":"object","properties":{"do":{"type":"string","const":"trophy.order"},"keys":{"maxItems":120,"type":"array","items":{"type":"string","maxLength":60}}},"required":["do","keys"]},{"type":"object","properties":{"do":{"type":"string","const":"board.save"},"board":{"$ref":"#/components/schemas/BoardInput"}},"required":["do","board"]},{"type":"object","properties":{"do":{"type":"string","const":"board.retire"},"key":{"type":"string","maxLength":60},"retired":{"type":"boolean"}},"required":["do","key","retired"]},{"type":"object","properties":{"do":{"type":"string","const":"board.delete"},"key":{"type":"string","maxLength":60}},"required":["do","key"]},{"type":"object","properties":{"do":{"type":"string","const":"key.make"},"name":{"default":"","type":"string","maxLength":80}},"required":["do"],"description":"A game key (p2g_...) for its own server: in the answer's made, shown once."},{"type":"object","properties":{"do":{"type":"string","const":"key.revoke"},"id":{"type":"string","maxLength":40}},"required":["do","id"]},{"type":"object","properties":{"do":{"type":"string","const":"test.reset"}},"required":["do"],"description":"Wipe what its developers earned testing it."},{"type":"object","properties":{"do":{"type":"string","const":"connect"},"on":{"type":"boolean"}},"required":["do","on"],"description":"Player Two Connect, for a game played on its own site."},{"type":"object","properties":{"do":{"type":"string","const":"rooms"},"on":{"type":"boolean"}},"required":["do","on"],"description":"The older switch: on is multiplayer rooms, off is none."},{"type":"object","properties":{"do":{"type":"string","const":"multiplayer"},"value":{"type":"string","enum":["rooms","none","own"]}},"required":["do","value"],"description":"How a game hosted on Player Two plays with others (Draft.multiplayer): Player Two's rooms through p2.rooms, single-player, or its own servers. Anything but rooms gives no new seats (409 rooms-off); rooms already open play on."}]},"ServicesView":{"type":"object","properties":{"slug":{"type":"string"},"hosted":{"type":"boolean","description":"Player Two hosts it: a web build it keeps, a site it follows, or a live download."},"connect":{"type":"object","properties":{"on":{"type":"boolean"},"hosted":{"type":"boolean"},"origin":{"type":["string","null"]},"why":{"type":["string","null"]}},"required":["on","hosted","origin","why"],"additionalProperties":{}},"trophies":{"type":"object","properties":{"trophies":{"type":"array","items":{}},"count":{"type":"integer"}},"required":["trophies","count"],"additionalProperties":{}},"boards":{"type":"object","properties":{"boards":{"type":"array","items":{}},"count":{"type":"integer"}},"required":["boards","count"],"additionalProperties":{}},"keys":{"type":"array","items":{},"description":"Its game keys, by name and first characters."},"rooms":{"$ref":"#/components/schemas/RoomsView"},"made":{"description":"key.make: the new key, shown this once.","type":"object","properties":{"token":{"type":"string"}},"required":["token"],"additionalProperties":{}},"reset":{}},"required":["slug","hosted","connect","trophies","boards","keys","rooms"],"additionalProperties":{}},"ServerScore":{"type":"object","properties":{"player":{"type":"string","maxLength":80,"description":"The player's id for this game."},"value":{"type":"integer"}},"required":["player","value"]},"ServerTrophy":{"type":"object","properties":{"player":{"type":"string","maxLength":80,"description":"The player's id for this game."}},"required":["player"]},"IdentityCheck":{"type":"object","properties":{"token":{"type":"string","maxLength":8192,"description":"The identity the client sent (p2.identity())."}},"required":["token"]},"Me":{"type":"object","properties":{"player":{"$ref":"#/components/schemas/GamePlayer"},"game":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},"session":{"type":"string"},"test":{"type":"boolean"},"demo":{"type":"boolean"},"from":{"type":"string"}},"required":["player","game","session","test","demo","from"]},"Done":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]},"AirAnswer":{"anyOf":[{"type":"object","properties":{"sdp":{"type":"string","description":"Realtime's WebRTC answer."},"key":{"type":"string","description":"This picture's key, for check."}},"required":["sdp","key"]},{"$ref":"#/components/schemas/Done"}]},"EventHeard":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"event":{"type":"string"}},"required":["ok","event"]},"IdentityToken":{"type":"object","properties":{"token":{"type":"string","description":"A JWT (RS256, typed p2-id) for this game only: the game's server checks it against /.well-known/p2-keys.json, or asks /api/v1/sdk/server/identity."},"exp":{"type":"number"}},"required":["token","exp"]},"InviteLink":{"type":"object","properties":{"url":{"type":"string"},"params":{}},"required":["url","params"]},"Seat":{"type":"object","properties":{"url":{"type":"string","description":"The rooms service's WebSocket address."},"ticket":{"type":"string","description":"Good for two minutes."},"exp":{"type":"number"}},"required":["url","ticket","exp"]},"TrophyList":{"type":"object","properties":{"trophies":{"type":"array","items":{"type":"object","properties":{"key":{"type":["string","null"]},"name":{"type":["string","null"]},"how":{"type":["string","null"]},"tier":{"type":"integer"},"xp":{"type":"integer"},"hidden":{"type":"boolean"},"art":{"type":"string"},"sign":{"type":["string","null"]},"earned":{"anyOf":[{"type":"object","properties":{"at":{"type":"number"},"n":{"anyOf":[{"type":"integer"},{"type":"null"}]},"test":{"type":"boolean"}},"required":["at","n"]},{"type":"null"}]},"share":{"type":"integer","description":"Its share of the game's players, in hundredths of a percent."},"rarity":{"type":"string","enum":["New","Common","Rare","Epic","Legendary","Mythic","Divine"]}},"required":["key","name","how","tier","xp","hidden","art","sign","earned","share","rarity"]}},"xp":{"type":"integer"},"level":{"type":"integer"},"progress":{"type":"object","properties":{"into":{"type":"integer"},"need":{"type":"integer"}},"required":["into","need"]}},"required":["trophies","xp","level","progress"]},"TrophyEarned":{"type":"object","properties":{"trophy":{"type":"object","properties":{"key":{"type":["string","null"]},"name":{"type":["string","null"]},"how":{"type":["string","null"]},"tier":{"type":"integer"},"xp":{"type":"integer"},"hidden":{"type":"boolean"},"art":{"type":"string"},"sign":{"type":["string","null"]},"earned":{"anyOf":[{"type":"object","properties":{"at":{"type":"number"},"n":{"anyOf":[{"type":"integer"},{"type":"null"}]},"test":{"type":"boolean"}},"required":["at","n"]},{"type":"null"}]},"share":{"type":"integer","description":"Its share of the game's players, in hundredths of a percent."},"rarity":{"type":"string","enum":["New","Common","Rare","Epic","Legendary","Mythic","Divine"]}},"required":["key","name","how","tier","xp","hidden","art","sign","earned","share","rarity"]},"new":{"type":"boolean"},"n":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Which holder of it the player is (none in test mode)."},"xp":{"type":"object","properties":{"gained":{"type":"integer"},"total":{"type":"integer"},"pending":{"type":"integer"}},"required":["gained","total"]},"level":{"type":"integer"},"progress":{"type":"object","properties":{"into":{"type":"integer"},"need":{"type":"integer"}},"required":["into","need"]},"complete":{"type":"object","properties":{"trophy":{"type":"object","properties":{"key":{"type":["string","null"]},"name":{"type":["string","null"]},"how":{"type":["string","null"]},"tier":{"type":"integer"},"xp":{"type":"integer"},"hidden":{"type":"boolean"},"art":{"type":"string"},"sign":{"type":["string","null"]},"earned":{"anyOf":[{"type":"object","properties":{"at":{"type":"number"},"n":{"anyOf":[{"type":"integer"},{"type":"null"}]},"test":{"type":"boolean"}},"required":["at","n"]},{"type":"null"}]},"share":{"type":"integer","description":"Its share of the game's players, in hundredths of a percent."},"rarity":{"type":"string","enum":["New","Common","Rare","Epic","Legendary","Mythic","Divine"]}},"required":["key","name","how","tier","xp","hidden","art","sign","earned","share","rarity"]},"n":{"anyOf":[{"type":"integer"},{"type":"null"}]},"xp":{"type":"integer"}},"required":["trophy","n","xp"]},"test":{"type":"boolean"}},"required":["trophy","new","n","xp","level","progress"]},"Stats":{"type":"object","properties":{"stats":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}}},"required":["stats"]},"StatsWritten":{"type":"object","properties":{"stats":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"unlocked":{"type":"array","items":{"$ref":"#/components/schemas/TrophyEarned"}}},"required":["stats","unlocked"]},"Scores":{"type":"object","properties":{"board":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"order":{"type":"string","enum":["high","low"]},"format":{"type":"string","enum":["number","time","points"]},"min":{"type":["number","null"]},"max":{"type":["number","null"]},"cooldown":{"type":"integer"},"trusted":{"type":"boolean"},"retired":{"type":"boolean"}},"required":["key","name","order","format","min","max","cooldown","trusted"]},"rows":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"integer"},"value":{"type":"number"},"at":{"type":"number"},"player":{"type":"object","properties":{"id":{"type":["string","null"]},"handle":{"type":["string","null"]},"name":{"type":"string"},"picture":{"type":["string","null"]}},"required":["id","handle","name"]},"me":{"type":"boolean"}},"required":["rank","value","at","player"]}},"total":{"type":"integer"},"me":{"anyOf":[{"type":"object","properties":{"rank":{"type":"integer"},"value":{"type":"number"}},"required":["rank","value"]},{"type":"null"}]}},"required":["board","rows","total","me"]},"ScoreKept":{"type":"object","properties":{"board":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"order":{"type":"string","enum":["high","low"]},"format":{"type":"string","enum":["number","time","points"]},"min":{"type":["number","null"]},"max":{"type":["number","null"]},"cooldown":{"type":"integer"},"trusted":{"type":"boolean"},"retired":{"type":"boolean"}},"required":["key","name","order","format","min","max","cooldown","trusted"]},"value":{"type":"number"},"best":{"type":"number"},"new":{"type":"boolean","description":"It is the player's best."},"previous":{"type":["number","null"]},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}]},"total":{"anyOf":[{"type":"integer"},{"type":"null"}]}},"required":["board","value","best","new","previous","rank","total"]},"SaveList":{"type":"object","properties":{"slots":{"type":"array","items":{"type":"object","properties":{"slot":{"type":"string"},"size":{"type":"integer"},"version":{"type":"integer"},"at":{"type":"number"}},"required":["slot","size","version","at"]}},"max":{"type":"integer"},"maxBytes":{"type":"integer"}},"required":["slots","max","maxBytes"]},"SaveRead":{"type":"object","properties":{"slot":{"type":"string"},"data":{"type":"string","description":"base64"},"size":{"type":"integer"},"version":{"type":"integer"},"at":{"type":"number"}},"required":["slot","data","size","version","at"]},"SaveKept":{"type":"object","properties":{"slot":{"type":"string"},"size":{"type":"integer"},"version":{"type":"integer"},"at":{"type":"number"}},"required":["slot","size","version","at"]},"SaveGone":{"type":"object","properties":{"deleted":{"type":"boolean","const":true},"slot":{"type":"string"}},"required":["deleted","slot"]},"MirrorRead":{"type":"object","properties":{"slot":{"type":"string"},"data":{"type":"string","description":"The copy, as JSON text."},"size":{"type":"integer"},"version":{"type":"integer"},"at":{"type":"number"}},"required":["slot","data","size","version","at"]},"AddonList":{"type":"object","properties":{"addons":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number"},"owned":{"type":"boolean"}},"required":["key","name","about","price","owned"]}},"owned":{"type":"array","items":{"type":"string"},"description":"The keys of every add-on the player has, retired ones too."}},"required":["addons","owned"]},"AddonOwned":{"type":"object","properties":{"key":{"type":"string"},"owned":{"type":"boolean"}},"required":["key","owned"]},"ProductInput":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":40,"description":"What the game's code asks for it by: small letters, numbers and dashes. It stays once a player has it."},"kind":{"description":"dlc (an add-on, sold on the game's page, split as its sales: 10% Player Two's), or an in-game purchase sold inside the game through p2.store (20% Player Two's): consumable, item, skin, mod, pack. dlc if not said; it stays once a player has it.","type":"string","enum":["dlc","consumable","item","skin","mod","pack"]},"qty":{"description":"A consumable's: how many one purchase gives.","type":"integer","minimum":1,"maximum":1000},"contents":{"description":"A pack's: the keys of 2 to 10 of the game's in-game products (no add-ons or packs).","minItems":2,"maxItems":10,"type":"array","items":{"type":"string","maxLength":40}},"name":{"type":"string","minLength":2,"maxLength":60},"about":{"type":"string","maxLength":280},"price":{"type":"number","minimum":0.99,"maximum":500,"description":"In US dollars, $0.99 to $500."},"retired":{"description":"true takes it off sale (its owners keep it); false puts it back.","type":"boolean"}},"required":["key","name","price"]},"ProductList":{"type":"object","properties":{"addons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"kind":{"type":"string","enum":["dlc","consumable","item","skin","mod","pack"]},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number"},"qty":{"type":"integer"},"contents":{"type":"array","items":{"type":"string"}},"retired":{"type":"boolean"},"owners":{"type":"integer","description":"How many players have it (tests not counted)."},"sold":{"type":"integer","description":"How many times it was bought."},"made":{"type":"number","description":"What the studio has made from it, in US dollars."}},"required":["id","key","kind","name","about","price","owners","sold","made"]}},"charges":{"type":"object","properties":{"sold":{"type":"integer"},"made":{"type":"number"}},"required":["sold","made"],"description":"The game's server's charges paid (p2.store.pay), tests not counted, and what the studio has made from them, in US dollars."},"paid":{"type":"boolean"}},"required":["addons","charges","paid"]},"StoreList":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"kind":{"type":"string","enum":["consumable","item","skin","mod","pack"],"description":"consumable (bought again and again, used up by the game's server), item and skin (the player's for good), mod (for good, a change to how the game plays), pack (several of the game's products)."},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number","description":"In US dollars."},"qty":{"description":"A consumable's: how many one purchase gives.","type":"integer"},"contents":{"description":"A pack's: the keys of the products it gives.","type":"array","items":{"type":"string"}},"owned":{"type":"boolean"},"count":{"description":"A consumable's: how many the player has.","type":"integer"}},"required":["id","key","kind","name","about","price","owned"]}},"owned":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string","enum":["consumable","item","skin","mod","pack"],"description":"consumable (bought again and again, used up by the game's server), item and skin (the player's for good), mod (for good, a change to how the game plays), pack (several of the game's products)."},"count":{"description":"A consumable's: how many the player has left.","type":"integer"},"test":{"description":"Bought as a test (only a test session sees these).","type":"boolean"}},"required":["key","kind"]}},"has":{"type":"boolean","description":"Whether the player has the game (bought, added free, given, a key or a backer pass): only they can buy. A test session always may."},"test":{"type":"boolean","description":"A test session (the game's team, a tester): it buys for nothing, as tests."}},"required":["products","owned","has","test"]},"StoreOwned":{"type":"object","properties":{"owned":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string","enum":["consumable","item","skin","mod","pack"],"description":"consumable (bought again and again, used up by the game's server), item and skin (the player's for good), mod (for good, a change to how the game plays), pack (several of the game's products)."},"count":{"description":"A consumable's: how many the player has left.","type":"integer"},"test":{"description":"Bought as a test (only a test session sees these).","type":"boolean"}},"required":["key","kind"]}},"test":{"type":"boolean"}},"required":["owned","test"]},"StoreReceiptAsk":{"type":"object","properties":{"payment":{"type":"string","maxLength":40,"description":"The payment Player Two's sheet made."}},"required":["payment"]},"StoreTestAsk":{"type":"object","properties":{"key":{"description":"A product's key (p2.store.buy).","type":"string","minLength":1,"maxLength":40},"charge":{"description":"Or a charge's id (p2.store.pay).","type":"string","maxLength":40}}},"StoreReceipt":{"anyOf":[{"type":"object","properties":{"receipt":{"type":"string","description":"A JWT typed p2-buy, signed RS256 with Player Two's keys (/.well-known/p2-keys.json), for this game, good for an hour."},"exp":{"type":"number"},"product":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string"},"n":{"type":"integer","description":"How many it gave (a consumable's number; 1 otherwise)."}},"required":["key","kind","n"]},"count":{"description":"A consumable's: how many the player has now.","type":"integer"},"test":{"type":"boolean"}},"required":["receipt","exp","product","test"]},{"type":"object","properties":{"receipt":{"type":"string","description":"A JWT typed p2-buy, signed RS256 with Player Two's keys (/.well-known/p2-keys.json), for this game, good for an hour."},"exp":{"type":"number"},"charge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ref":{"description":"The game's own reference for it, as its server gave it.","type":["string","null"]}},"required":["id","name","ref"]},"amount":{"type":"number","description":"What was paid, in US dollars (0 for a test)."},"test":{"type":"boolean"}},"required":["receipt","exp","charge","amount","test"]}]},"ChargeInput":{"type":"object","properties":{"player":{"description":"The player's id for this game (or send their session token in x-player-session).","type":"string","maxLength":40},"name":{"type":"string","maxLength":60,"minLength":2,"description":"What it is, as the player reads it on Player Two's card: 2 to 60 characters (422 bad-name otherwise)."},"about":{"description":"A line more about it, on the card.","type":"string","maxLength":280},"price":{"type":"number","minimum":0.1,"maximum":500,"description":"In US dollars, $0.10 to $500 (422 bad-price otherwise); rounded to the cent."},"ref":{"description":"Your own reference for it (an item, an order): handed back as given with the charge, its receipt and its webhook.","type":"string","maxLength":200}},"required":["name","price"]},"ChargeAnswer":{"type":"object","properties":{"charge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number","description":"In US dollars."},"ref":{"type":["string","null"]},"player":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"state":{"type":"string","enum":["open","paid","cancelled","expired"],"description":"open: it can be paid; paid: paid once; cancelled: your server withdrew it; expired: not paid within 30 minutes."},"test":{"type":"boolean","description":"Paid as a test, for nothing (a test session's)."},"payment":{"description":"The payment that paid it (empty for a test or until paid).","type":["string","null"]},"created":{"type":"number"},"expires":{"type":"number"},"paid":{"type":["number","null"]}},"required":["id","name","about","price","ref","player","state","test","payment","created","expires","paid"]}},"required":["charge"]},"ChargeList":{"type":"object","properties":{"player":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},{"$ref":"#/components/schemas/GamePlayer"}]},"charges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number","description":"In US dollars."},"ref":{"type":["string","null"]},"player":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"state":{"type":"string","enum":["open","paid","cancelled","expired"],"description":"open: it can be paid; paid: paid once; cancelled: your server withdrew it; expired: not paid within 30 minutes."},"test":{"type":"boolean","description":"Paid as a test, for nothing (a test session's)."},"payment":{"description":"The payment that paid it (empty for a test or until paid).","type":["string","null"]},"created":{"type":"number"},"expires":{"type":"number"},"paid":{"type":["number","null"]}},"required":["id","name","about","price","ref","player","state","test","payment","created","expires","paid"]},"description":"Newest first."},"next":{"description":"More: ask again with before=this. Empty when this is the last page.","type":["number","null"]},"test":{"type":"boolean","description":"The player's latest session is a test: their test charges are listed too."}},"required":["player","charges","next","test"]},"ChargeForPlayer":{"type":"object","properties":{"charge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number","description":"In US dollars."},"ref":{"type":["string","null"]},"player":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"state":{"type":"string","enum":["open","paid","cancelled","expired"],"description":"open: it can be paid; paid: paid once; cancelled: your server withdrew it; expired: not paid within 30 minutes."},"test":{"type":"boolean","description":"Paid as a test, for nothing (a test session's)."},"payment":{"description":"The payment that paid it (empty for a test or until paid).","type":["string","null"]},"created":{"type":"number"},"expires":{"type":"number"},"paid":{"type":["number","null"]}},"required":["id","name","about","price","ref","player","state","test","payment","created","expires","paid"]},"test":{"type":"boolean","description":"A test session (the game's team, a tester): it pays for nothing, as a test."}},"required":["charge","test"]},"ServerStore":{"type":"object","properties":{"player":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},{"$ref":"#/components/schemas/GamePlayer"}]},"owned":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string","enum":["consumable","item","skin","mod","pack"],"description":"consumable (bought again and again, used up by the game's server), item and skin (the player's for good), mod (for good, a change to how the game plays), pack (several of the game's products)."},"count":{"description":"A consumable's: how many the player has left.","type":"integer"},"test":{"description":"Bought as a test (only a test session sees these).","type":"boolean"}},"required":["key","kind"]}},"test":{"type":"boolean"}},"required":["player","owned","test"]},"StoreUse":{"type":"object","properties":{"player":{"description":"The player's id for this game (or send their session token in x-player-session).","type":"string","maxLength":40},"key":{"type":"string","minLength":1,"maxLength":40,"description":"The consumable's key."},"n":{"description":"How many to use up: 1 if not said.","type":"integer","minimum":1,"maximum":1000}},"required":["key"]},"StoreUsed":{"type":"object","properties":{"player":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},{"$ref":"#/components/schemas/GamePlayer"}]},"key":{"type":"string"},"used":{"type":"integer"},"count":{"type":"integer","description":"How many they have left."}},"required":["player","key","used","count"]},"ReceiptCheck":{"type":"object","properties":{"receipt":{"type":"string","minLength":10,"maxLength":4000,"description":"The receipt the client sent (p2.store.buy or p2.store.pay)."}},"required":["receipt"]},"ReceiptChecked":{"anyOf":[{"type":"object","properties":{"valid":{"type":"boolean","const":true},"player":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"product":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string"},"n":{"type":"integer"}},"required":["key","kind","n"]},"count":{"type":"integer"},"payment":{"description":"Empty for a test.","type":["string","null"]},"amount":{"description":"What was paid, in US dollars; empty for a test.","type":["number","null"]},"test":{"type":"boolean"},"at":{"type":"number"},"exp":{"type":"number"}},"required":["valid","player","product","payment","amount","test","at","exp"]},{"type":"object","properties":{"valid":{"type":"boolean","const":true},"player":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"charge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ref":{"description":"The game's own reference for it, as its server gave it.","type":["string","null"]}},"required":["id","name","ref"]},"payment":{"description":"Empty for a test.","type":["string","null"]},"amount":{"description":"What was paid, in US dollars; empty for a test.","type":["number","null"]},"test":{"type":"boolean"},"at":{"type":"number"},"exp":{"type":"number"}},"required":["valid","player","charge","payment","amount","test","at","exp"]},{"type":"object","properties":{"valid":{"type":"boolean","const":false},"code":{"type":"string","enum":["malformed","unknown-key","bad-signature","expired","wrong-game","not-paid","not-a-receipt"]},"why":{"type":"string"}},"required":["valid","code","why"]}]},"CloudSave":{"type":"object","properties":{"folders":{"type":"array","items":{},"description":"The save folders its developer named for this system."},"save":{"anyOf":[{"type":"object","properties":{"version":{"type":"integer"},"size":{"type":"integer"},"at":{"type":"number"},"url":{"type":"string"}},"required":["version","size","at","url"]},{"type":"null"}]}},"required":["folders","save"]},"CloudKept":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"url":{"type":"string","description":"Where to PUT the zip (fifteen minutes)."}},"required":["key","url"]},{"type":"object","properties":{"version":{"type":"integer"},"size":{"type":"integer"},"at":{"type":"number"}},"required":["version","size","at"]}]},"ItemList":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["skin","avatar","wear","emote","effect"]},"tier":{"type":"string","enum":["common","rare","epic","legendary","mythic","divine"]},"slot":{"description":"Where it goes: head, face, neck or back for a wearable; skin, avatar or effect; none for an emote.","type":["string","null"]},"worn":{"type":"boolean","description":"Whether the player has it on."},"number":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"A Divine item's number, of the 77 made."},"picture":{"description":"An SVG of it; none for an emote or an effect.","type":["string","null"]}},"required":["key","name","kind","tier","slot","worn","number","picture"]}}},"required":["items"]},"Shop":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string","enum":["skin","avatar","wear","emote","effect"]},"name":{"type":"string"},"tier":{"type":"string","enum":["common","rare","epic","legendary","mythic","divine"]},"tierName":{"type":"string"},"price":{"type":"number","description":"In US dollars: the tier's price (Common is free)."},"line":{"type":"string"},"slot":{"type":["string","null"]},"level":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"A Common item: the level that unlocks it."},"edition":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"A Divine item: how many are made (77)."},"left":{"anyOf":[{"type":"integer"},{"type":"null"}]},"owned":{"type":"boolean"},"number":{"anyOf":[{"type":"integer"},{"type":"null"}]}},"required":["key","kind","name","tier","tierName","price","line","slot","level","edition","left","owned","number"]}},"worn":{"$ref":"#/components/schemas/Worn"},"level":{"anyOf":[{"type":"integer"},{"type":"null"}]},"mods":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"game":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"about":{"type":"string"},"price":{"type":"number"},"owned":{"type":"boolean"},"base":{"type":"boolean","description":"Whether the player has the game it needs."}},"required":["slug","game","key","name","about","price","owned","base"]}}},"required":["items","worn","level","mods"]},"Worn":{"type":"object","properties":{"avatar":{"type":["string","null"]},"skin":{"type":["string","null"]},"wear":{"type":"array","items":{"type":"string"}},"effect":{"type":["string","null"]}},"required":["avatar","skin","wear","effect"],"description":"What a player has on: an avatar (else their own critter), a skin, a wearable on each slot, an effect."},"WornBody":{"type":"object","properties":{"worn":{"$ref":"#/components/schemas/Worn"}},"required":["worn"]},"WornPlayers":{"type":"object","properties":{"players":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"worn":{"$ref":"#/components/schemas/Worn"},"emotes":{"type":"array","items":{"type":"string"}}},"required":["worn","emotes"]}}},"required":["players"]},"ServerSession":{"type":"object","properties":{"player":{"$ref":"#/components/schemas/GamePlayer"},"game":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},"session":{"type":"string"},"test":{"type":"boolean"},"demo":{"type":"boolean"},"from":{"type":"string"},"live":{"type":"boolean"},"startedAt":{"type":"number"},"beatAt":{"type":"number"}},"required":["player","game","session","test","demo","from","live","startedAt","beatAt"]},"ServerAddons":{"type":"object","properties":{"player":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},{"$ref":"#/components/schemas/GamePlayer"}]},"live":{"type":"boolean"},"addons":{"type":"array","items":{"type":"string"},"description":"The keys of the add-ons the player has."}},"required":["player","addons"]},"IdentityChecked":{"anyOf":[{"type":"object","properties":{"valid":{"type":"boolean","const":true},"player":{"type":"object","properties":{"id":{"type":"string"},"handle":{"type":"string"},"name":{"type":"string"},"picture":{"type":["string","null"]},"level":{"type":"integer"}},"required":["id","handle","name","picture","level"]},"kind":{"type":"string","enum":["full","demo","test"]},"game":{"type":"object","properties":{"slug":{"type":"string"}},"required":["slug"]},"session":{"type":"string"},"live":{"type":"boolean"},"exp":{"type":"number"}},"required":["valid","player","kind","game","session","live","exp"]},{"type":"object","properties":{"valid":{"type":"boolean","const":false},"code":{"type":"string"},"why":{"type":"string"},"player":{"type":"null"},"kind":{"type":"null"}},"required":["valid","code","why","player","kind"]}]},"Avatar":{"type":"object","properties":{"id":{"type":"string","description":"The player's id for this game (p2.player.id)."},"handle":{"type":"string"},"name":{"type":"string"},"level":{"type":"integer"},"picture":{"type":["string","null"]},"critter":{"type":"object","properties":{"face":{"type":"string","description":"The name their critter is drawn from: the same name, the same critter, everywhere on Player Two."},"size":{"type":"integer","description":"The critter's box: 18 pixels a side."},"tile":{"type":"string","description":"The colour of the tile it stands on in Player Two's own pages."},"pixels":{"type":"array","items":{"type":"array","prefixItems":[{"type":"integer"},{"type":"integer"},{"type":"string"}],"items":false,"minItems":3,"maxItems":3,"description":"[x, y, colour]: x and y from the top left of the 18 by 18 box, the colour as CSS (#rrggbb or hsl())."},"description":"Every pixel of it, eyes open."},"model":{"type":"string","description":"The same critter as a 3D model, a glTF binary (.glb): a pixel 7 cm a side and deep, as in the Plaza, standing on the origin and facing +Z, coloured by its vertices. Anyone may fetch it (CORS *); it never changes."}},"required":["face","size","tile","pixels","model"]},"worn":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"slot":{"type":"string"},"model":{"type":["string","null"]},"picture":{"type":["string","null"]}},"required":["key","name","slot","model","picture"],"additionalProperties":{}},"description":"What they wear (a hat, a trail, a badge): each a model and a picture. Empty for now."}},"required":["id","handle","name","level","picture","critter","worn"]},"TravelAsk":{"anyOf":[{"type":"object","properties":{"to":{"type":"string","maxLength":64,"description":"The game to go to: its address on Player Two (playertwo.lol/game/{to})."},"params":{"description":"What the game gone to is given as p2.params (invite params: up to 16 strings, 512 bytes). from (this game's address) and place are added."},"place":{"description":"Where in that game, as it names its places (p2.params.place).","anyOf":[{"type":"string","maxLength":40,"pattern":"^[A-Za-z0-9_-]+$"},{"type":"null"}]},"follow":{"description":"After the party's leader (Player Two's player says so).","type":"boolean"}},"required":["to"]},{"type":"object","properties":{"home":{"type":"boolean","const":true,"description":"Back to the Plaza."}},"required":["home"]}]},"TravelGone":{"anyOf":[{"type":"object","properties":{"to":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},"params":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"What it is given as p2.params: from, place and the game's own."},"unlocked":{"type":"array","items":{"$ref":"#/components/schemas/TrophyEarned"},"description":"Player Two's own trophies the travel earned (Through five portals)."}},"required":["to","params","unlocked"]},{"type":"object","properties":{"to":{"type":"object","properties":{"slug":{"type":"null"},"title":{"type":"string"},"url":{"type":"string"}},"required":["slug","title","url"]}},"required":["to"]}]},"TravelLinks":{"type":"object","properties":{"home":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"}},"required":["title","url"],"description":"The Plaza: p2.travel.home()."},"links":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"tagline":{"type":"string"},"art":{"type":"string","description":"Its key art (square), or its vertical poster."},"players":{"type":"integer","description":"Players in it now."},"friends":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"}},"required":["handle","name"]},"description":"Up to five of the player's friends in it now (those who show what they play)."},"why":{"type":"string","enum":["party","friends","recent","popular"],"description":"Why it is here: the party's leader brought the party to it, friends are in it, the player played it lately, or it is busy."},"free":{"type":"boolean"},"owned":{"type":"boolean","description":"In the player's library. A paid game they don't own opens with its demo, or Buy."}},"required":["slug","title","tagline","art","players","friends","why","free","owned"]},"description":"Up to 12 doors, in that order: only games playable in Player Two's player, out and listed, never this one."}},"required":["home","links"]},"RoomMet":{"type":"object","properties":{"players":{"maxItems":100,"type":"array","items":{"type":"string","maxLength":40},"description":"The ids (for this game) of the players in the room with this one for the last minute."}},"required":["players"]},"MetCounted":{"type":"object","properties":{"counted":{"type":"integer","description":"How many were counted: those with a live session of this game now, not blocked either way."}},"required":["counted"]},"MatchReport":{"type":"object","properties":{"players":{"minItems":2,"maxItems":64,"type":"array","items":{"anyOf":[{"type":"object","properties":{"token":{"type":"string","maxLength":8192,"description":"The identity the player's client sent the server (p2.identity(): taken up to six hours past its time), or their play session's token (p2s_..., a computer's client)."}},"required":["token"]},{"type":"object","properties":{"id":{"type":"string","maxLength":80,"description":"Or the player's id for this game (the identity's sub), as the game's server already names them for trophies: a player with a session of this game in the last six hours."}},"required":["id"]}]}},"minutes":{"description":"How long they played together (1 when not said).","type":"number","minimum":1,"maximum":600},"id":{"description":"The game's own id for the match: reported once (409 reported again).","type":"string","maxLength":80},"mode":{"description":"The match's mode, for the game's own records (not kept).","type":"string","maxLength":40}},"required":["players"]},"MatchCounted":{"type":"object","properties":{"counted":{"type":"integer","description":"Pairs written: each player's list gets each other player."},"players":{"type":"array","items":{"type":"object","properties":{"id":{"description":"Their id for this game, when the token said.","type":["string","null"]},"counted":{"type":"boolean"},"why":{"description":"Why not: malformed, bad-signature, expired, wrong-game, guest, gone, unknown-session, unknown-player.","type":"string"}},"required":["id","counted"]}}},"required":["counted","players"]},"PartyTravel":{"type":"object","properties":{"party":{"anyOf":[{"type":"object","properties":{"leader":{"type":"boolean"},"went":{"anyOf":[{"type":"object","properties":{"by":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"}},"required":["handle","name"]},"home":{"type":"boolean","description":"Back to the Plaza."},"to":{"anyOf":[{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},{"type":"null"}]},"params":{"description":"What to follow them with: the leader's join params there now, else what they travelled with.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"at":{"type":"number","description":"When (milliseconds since 1970)."}},"required":["by","home","to","at"]},{"type":"null"}],"description":"Where the party's leader went in the last two minutes."}},"required":["leader","went"]},{"type":"null"}]}},"required":["party"]},"PortalIn":{"type":"object","properties":{"to":{"type":"string","maxLength":64,"description":"The game walked into."},"via":{"type":"string","enum":["plaza","follow"],"description":"From one of the Plaza's screens, or after the party's leader."}},"required":["to","via"]},"PortalCounted":{"type":"object","properties":{"to":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"}},"required":["slug","title"]},"unlocked":{"type":"array","items":{"$ref":"#/components/schemas/TrophyEarned"}}},"required":["to","unlocked"]},"GameRead":{"type":"object","properties":{"sub":{"type":"object","properties":{"v":{"type":"number","const":1},"slug":{"type":"string"},"createdAt":{"type":"number","description":"When it was listed (milliseconds since 1970)."},"draft":{"$ref":"#/components/schemas/DraftKept"}},"required":["v","slug","createdAt","draft"]},"pledges":{"type":"array","items":{}},"real":{"type":"object","properties":{"studio":{"type":"object","properties":{"name":{"type":"string"},"handle":{"type":"string"},"since":{"type":"number"},"bio":{"type":"string"},"base":{"type":"string"},"sign":{"type":"string"},"site":{"type":"string"},"x":{"type":"string"},"discord":{"type":"string"},"youtube":{"type":"string"},"github":{"anyOf":[{"type":"object","properties":{"username":{"type":"string"},"at":{"type":"number"},"days":{"type":"array","items":{"type":"number"}},"levels":{"type":"array","items":{"type":"number"}},"total":{"type":"number"}},"required":["username","at","days","levels","total"],"additionalProperties":{}},{"type":"null"}]},"verified":{"type":"boolean","description":"Player Two has checked the developer's identity."},"shipped":{"type":"integer"},"followers":{"type":"integer"}},"required":["name","handle","since","bio","base","sign","site","x","discord","youtube","github","verified"],"additionalProperties":{}},"raise":{"type":"object","properties":{"raised":{"type":"number"},"backers":{"type":"number"},"goal":{"type":"number"},"opensAt":{"type":"number"},"closesAt":{"type":"number"},"state":{"type":"string"},"history":{"type":"array","items":{"type":"number"}},"fundedAt":{"type":["number","null"]}},"required":["raised","backers","goal","opensAt","closesAt","state","history","fundedAt"],"additionalProperties":{}},"release":{"description":"A game not out yet: the day it comes out (Wishlist in place of Play until then, and Pre-order for a paid game), whether its page counts down, and whether players may ask to playtest it. Absent once it is out.","type":"object","properties":{"at":{"type":["number","null"]},"private":{"type":"boolean"},"countdown":{"type":"boolean"},"asks":{"type":"boolean"},"preorders":{"description":"A paid game coming on its day is pre-ordered: how many, and what they hold.","type":"object","properties":{"count":{"type":"integer"},"held":{"type":"number","description":"Dollars, held by Player Two until its day."}},"required":["count","held"]}},"required":["at","private","countdown","asks"]},"testing":{"description":"A game out with a build out to its testers first: its team and testers play that one, its players the live one.","type":"boolean"},"stages":{"type":"array","items":{"type":"object","properties":{"ord":{"type":"number"},"title":{"type":"string"},"state":{"type":"string"}},"required":["ord","title","state"],"additionalProperties":{}}},"votes":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"number"},"state":{"type":"string"}},"required":["stage","state"],"additionalProperties":{}}},"updates":{"type":"array","items":{"type":"object","properties":{"at":{"type":"number"},"title":{"type":"string"},"body":{"type":"string"},"md":{"description":"Its words are Markdown (its team's posts since 2026-11-07); otherwise plain words.","type":"boolean"},"media":{"description":"Its pictures and video, in order.","type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video"]},"src":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"},"still":{"type":"string"},"copies":{"type":"object","properties":{"w":{"type":"integer"},"at":{"type":"array","items":{"type":"object","properties":{"w":{"type":"integer"},"src":{"type":"string"}},"required":["w","src"]}}},"required":["w","at"]}},"required":["kind","src"]}},"edited":{"description":"When its team last edited it: its page says Edited.","type":"number"},"pinned":{"description":"When it was pinned: first in the Updates.","type":"number"}},"required":["at","title","body"],"additionalProperties":{}}},"feed":{"type":"array","items":{"type":"object","properties":{"who":{"type":"string"},"amount":{"type":"number"},"at":{"type":"number"}},"required":["who","amount","at"],"additionalProperties":{}}},"copies":{"type":"number"},"gross":{"type":"number"},"price":{"type":"number","description":"What it costs in the store now: on a sale, the sale's price."},"sale":{"type":"object","properties":{"percent":{"type":"number"},"was":{"type":"number","description":"The price before the sale."},"ends":{"type":"number"}},"required":["percent","was","ends"],"additionalProperties":{}},"prices":{"type":"array","items":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}],"items":false,"minItems":2,"maxItems":2}},"low30":{"type":"number"},"wishlists":{"type":"number"},"plays":{"type":"number"},"hours":{"type":"number"},"inGame":{"type":"number"},"recent":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"}},"required":["handle"],"additionalProperties":{}}},"players":{"type":"number"},"playerDays":{"type":"array","items":{"type":"number"}},"removed":{"type":"boolean"},"archived":{"type":"boolean"},"featured":{"type":["number","null"]},"reviews":{"type":"object","properties":{"n":{"type":"number"},"yes":{"type":"number"}},"required":["n","yes"],"additionalProperties":{}}},"required":["studio","stages","votes","updates","feed","copies","gross","price","wishlists","plays","players","playerDays","removed"],"additionalProperties":{}}},"required":["sub","pledges","real"]},"GameList":{"type":"object","properties":{"games":{"type":"array","items":{"$ref":"#/components/schemas/GameRead"}}},"required":["games"]},"LiveCounts":{"type":"object","properties":{"games":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer"},"description":"Players in each game now, by slug; a game with nobody in it isn't listed."}},"required":["games"]},"PlayerPublic":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"},"joined":{"type":"number"},"moved":{},"xp":{"type":"integer"},"level":{"type":"integer"},"bio":{"type":"string"},"hue":{"anyOf":[{},{"type":"null"}]},"links":{},"site":{"type":"string"},"followers":{"type":"integer"},"following":{"type":"integer"},"followed":{"type":"boolean"},"me":{"type":"boolean"},"stats":{"type":"object","properties":{"library":{"type":"integer"},"wishlist":{"type":"integer"},"passes":{"type":"integer"},"reviews":{"type":"integer"}},"required":["library","wishlist","passes","reviews"]},"standing":{},"activity":{"anyOf":[{"type":"array","items":{}},{"type":"null"}]},"friends":{"anyOf":[{"type":"object","properties":{"n":{"type":"integer"},"mutual":{"type":"integer"},"list":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":["string","null"]}},"required":["handle","name"],"additionalProperties":{}}}},"required":["n","mutual","list"],"additionalProperties":{}},{"type":"null"}]},"friend":{},"collections":{},"private":{"type":"object","properties":{"standing":{"type":"boolean"},"activity":{"type":"boolean"},"friendsOnly":{"type":"boolean"},"hidden":{"type":"boolean"}},"required":["standing","activity","friendsOnly","hidden"]}},"required":["handle","name","joined","xp","level","bio","hue","links","site","followers","following","followed","me","stats","standing","activity","friends","friend","collections","private"],"additionalProperties":{}},"StudioRead":{"type":"object","properties":{"studio":{"type":"object","properties":{"name":{"type":"string"},"handle":{"type":"string"},"since":{"type":"number"},"bio":{"type":"string"},"base":{"type":"string"},"sign":{"type":"string"},"site":{"type":"string"},"x":{"type":"string"},"discord":{"type":"string"},"youtube":{"type":"string"},"github":{"anyOf":[{"type":"object","properties":{"username":{"type":"string"},"at":{"type":"number"},"days":{"type":"array","items":{"type":"number"}},"levels":{"type":"array","items":{"type":"number"}},"total":{"type":"number"}},"required":["username","at","days","levels","total"],"additionalProperties":{}},{"type":"null"}]},"verified":{"type":"boolean","description":"Player Two has checked the developer's identity."},"shipped":{"type":"integer"},"followers":{"type":"integer"}},"required":["name","handle","since","bio","base","sign","site","x","discord","youtube","github","verified","followers"],"additionalProperties":{}}},"required":["studio"]},"McpMessage":{"type":"object","properties":{"jsonrpc":{"type":"string","const":"2.0"},"id":{"type":["string","number","null"]},"method":{"type":"string"},"params":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["jsonrpc"],"additionalProperties":{}}},"securitySchemes":{"agentKey":{"type":"http","scheme":"bearer","bearerFormat":"p2_agent_...","description":"A player's agent key: they make one in their studio on playertwo.lol, under Agents (shown once). It submits and edits that player's games and nothing else: no payments, no account settings, never an admin's powers."},"playSession":{"type":"http","scheme":"bearer","bearerFormat":"p2s_...","description":"A play session's token: the game's own calls while a player plays it (the SDK sends it). Made by POST /api/v1/games/{slug}/session as the game starts, alive while the game beats once a minute."},"gameKey":{"type":"http","scheme":"bearer","bearerFormat":"p2g_...","description":"A game's key, for its own server only: its developer makes one in their studio (the game's services). Never put it in a game's client."},"session":{"type":"apiKey","in":"cookie","name":"p2-session","description":"A player signed in on playertwo.lol (the session cookie; a Privy access token as Authorization: Bearer works too)."}}}}