triggair
API

API reference

Every endpoint, its auth scope, and a request/response example. Most games use the @triggair/sdk client instead of raw HTTP — this is the underlying contract. Machine-readable: /openapi.json (OpenAPI 3.1). Auth scopes: a publishable key goes in X-Triggair-Key; a player token and the developer/operator sessions go in Authorization: Bearer.

Players

POST /v1/players/anonymous publishable key

Mint an anonymous player token

Exchanges a stable device id for a 24h player token. The player is created on first use. This is what the SDK's tg.login() calls; every player-scoped call sends the returned token as `Authorization: Bearer`.

request
{
  "device_id": "a-stable-device-uuid",
  "turnstile_token": "optional-if-challenge-on"
}
response · A banned account or device is refused with 403 player_banned.
{
  "player_id": "p_1a2b3c",
  "token": "eyJ…",
  "expires_in": 86400
}
POST /v1/players/recover publishable key

Recover a player on a new device

Consumes a recovery code (from /v1/players/me/recovery-code) to rebind the same player to a new device, returning a fresh token.

request
{
  "code": "RECOVERY-CODE",
  "device_id": "new-device-uuid"
}
response
{
  "player_id": "p_1a2b3c",
  "token": "eyJ…",
  "expires_in": 86400
}
GET /v1/players/me player token

Get the current player

response
{
  "player_id": "p_1a2b3c",
  "display_name": null,
  "created_at": "2026-07-01T00:00:00Z"
}
POST /v1/players/me/recovery-code player token

Mint a cross-device recovery code

Returns a single-use code the player saves/shares once; redeem it later with /v1/players/recover on another device.

response
{
  "code": "RECOVERY-CODE",
  "expires_at": "2026-08-01T00:00:00Z"
}
GET /v1/players/me/moderation player token

Get the caller's own ban/mute status

The player's active bans and restrictions (shadow actions are hidden from self-view).

response
{
  "banned": false,
  "restrictions": []
}
GET /v1/players/me/stats player token

Get the player's own stats

response
{
  "stats": [
    {
      "key": "kills",
      "value": 12
    }
  ]
}
POST /v1/players/me/stats player token

Update stats (set / increment)

Applies one or more stat operations atomically. Stats back leaderboards, achievements, and segment targeting.

request
{
  "ops": [
    {
      "key": "kills",
      "op": "increment",
      "value": 1
    }
  ]
}
response
{
  "stats": [
    {
      "key": "kills",
      "value": 13
    }
  ]
}
PATCH /v1/players/me player token

Update the player's profile (e.g. display name)

request
{
  "display_name": "AceRunner"
}
response
{
  "player_id": "p_1a2b3c",
  "display_name": "AceRunner"
}
GET /v1/players player token

Look up players (public fields)

  • ids (query) — Comma-separated player ids.
response
{
  "players": [
    {
      "player_id": "p_9",
      "display_name": "Rival"
    }
  ]
}
GET /v1/players/:id player token

Get another player's public profile

  • id (path) — Player id.
response
{
  "player_id": "p_9",
  "display_name": "Rival"
}

Saves

GET /v1/saves player token

List the player's save slots

response
{
  "saves": [
    {
      "slot": "slot1",
      "version": 3,
      "updated_at": "2026-07-01T00:00:00Z"
    }
  ]
}
GET /v1/saves/:slot player token

Read a save slot

  • slot (path) — Slot name (game-defined).
response
{
  "slot": "slot1",
  "data": {
    "level": 4,
    "coins": 120
  },
  "version": 3
}
PUT /v1/saves/:slot player token

Write a save slot

Last-write-wins by default. Send `If-Match: <version>` for optimistic concurrency — a stale version returns 409 save_conflict. Returns 201 on first write, 200 on update.

  • slot (path) — Slot name.
request
{
  "data": {
    "level": 5,
    "coins": 90
  }
}
response
{
  "slot": "slot1",
  "data": {
    "level": 5,
    "coins": 90
  },
  "version": 4
}
DELETE /v1/saves/:slot player token

Delete a save slot

  • slot (path) — Slot name.
response · 204 No Content.
null

Leaderboards

POST /v1/leaderboards/:board/scores player token

Submit a score

Submits a score to a configured board. The board's aggregation (best/last/sum) and period (all-time/daily/weekly) are set by the developer; the response reports the player's kept score for the current period.

  • board (path) — Board key.
request
{
  "score": 9000
}
response
{
  "ok": true,
  "best_score": 9000,
  "period_key": "2026-07"
}
GET /v1/leaderboards/:board/top player token

Read the top entries

  • board (path) — Board key.
  • limit (query) — Max entries (default 10).
response
{
  "board": "high_scores",
  "period_key": "2026-07",
  "entries": [
    {
      "rank": 1,
      "player_id": "p_9",
      "score": 12000
    }
  ]
}
GET /v1/leaderboards/:board/around-me player token

Read entries around the player

  • board (path) — Board key.
response
{
  "board": "high_scores",
  "period_key": "2026-07",
  "me": {
    "rank": 42,
    "score": 8000
  },
  "entries": []
}
GET /v1/leaderboards/:board/friends player token

Read the friends-only board

  • board (path) — Board key.
response
{
  "board": "high_scores",
  "period_key": "2026-07",
  "me": {
    "rank": 2,
    "score": 8000
  },
  "entries": []
}

Achievements

GET /v1/achievements player token

List achievements + the caller's progress

Definitions plus this player's progress; secret achievements are hidden until unlocked.

response
{
  "achievements": [
    {
      "key": "first_win",
      "unlocked": true,
      "progress": 1,
      "target": 1
    }
  ]
}
POST /v1/achievements/:key/progress player token

Report achievement progress

The server clamps progress, unlocks exactly once, and escrows any reward into the inbox. The client reports progress but never grants the reward.

  • key (path) — Achievement key.
request
{
  "amount": 1
}
response
{
  "key": "first_win",
  "unlocked": true,
  "progress": 1
}

Daily

GET /v1/daily player token

Get daily-reward status

response
{
  "claimable": true,
  "streak": 3,
  "next_claim_at": "2026-07-11T00:00:00Z"
}
POST /v1/daily/claim player token

Claim today's reward

Server-day gated; the reward is escrowed into the inbox. Re-claiming the same day → 409 conflict.

response
{
  "claimed": true,
  "streak": 4
}

Inbox

GET /v1/inbox player token

List inbox items

  • limit (query) — Max items.
response
{
  "items": [
    {
      "id": "in_1",
      "kind": "reward",
      "claimable": true,
      "read": false
    }
  ]
}
POST /v1/inbox/:id/read player token

Mark an inbox item read

  • id (path) — Inbox item id.
response
{
  "ok": true
}
POST /v1/inbox/:id/claim player token

Claim an inbox item's reward

The single hardened grant path — exactly-once (idempotent per item). Grants currency, items, or stat rewards.

  • id (path) — Inbox item id.
response
{
  "claimed": true,
  "granted": {
    "currency": [
      {
        "code": "gold",
        "amount": 100
      }
    ]
  }
}

Social

GET /v1/friends player token

List friends

response
{
  "friends": [
    {
      "player_id": "p_9",
      "display_name": "Rival",
      "state": "accepted"
    }
  ]
}
GET /v1/friends/requests player token

List incoming friend requests

response
{
  "requests": [
    {
      "player_id": "p_7",
      "display_name": "Newcomer"
    }
  ]
}
POST /v1/friends/:id player token

Send or accept a friend request

  • id (path) — The other player's id.
response
{
  "state": "pending"
}
DELETE /v1/friends/:id player token

Remove a friend / cancel a request

  • id (path) — The other player's id.
response · 204 No Content.
null
POST /v1/friends/:id/block player token

Block a player

  • id (path) — The player to block.
response
{
  "blocked": true
}
POST /v1/share player token

Mint a share / invite link

Creates a short code carrying an opaque context blob (a level, a challenge, a referral). Resolve it with GET /v1/share/:code on the recipient's device.

request
{
  "context": {
    "level": 7
  },
  "expires_in_seconds": 604800
}
response
{
  "code": "SH4RE",
  "expires_at": "2026-07-18T00:00:00Z"
}
GET /v1/share/:code player token

Resolve a share link

  • code (path) — Share code.
response
{
  "context": {
    "level": 7
  },
  "from": "p_1a2b3c"
}

Moderation

POST /v1/moderate/check player token

Pre-check text before submit

Runs the same moderation the write paths use, so you can validate a chosen name/message without a round-trip failure. Stateless (writes nothing).

request
{
  "surface": "username",
  "text": "player name to check"
}
response
{
  "verdict": "mask",
  "masked_text": "player ****",
  "categories": [
    "profanity"
  ],
  "severity": 2
}
POST /v1/reports player token

Report a player / message / UGC

request
{
  "target_type": "player",
  "target_id": "p_9",
  "reason": "harassment",
  "note": "optional"
}
response
{
  "id": "cr_1",
  "state": "open"
}
POST /v1/appeals player token

Appeal a ban

request
{
  "ban_id": "bn_1",
  "body": "why the ban should be lifted"
}
response
{
  "id": "ap_1",
  "state": "pending"
}

Compliance

POST /v1/players/me/age player token

Set the player's age bracket

Neutral age screen. A birth year is mapped to a bracket and DISCARDED (no DOB stored). Returns the compliance view with the gated-feature map.

request
{
  "birth_year": 2013
}
response
{
  "bracket": "13_15",
  "consent_state": "pending",
  "gated": {
    "open_chat": true,
    "lootbox": true
  }
}
GET /v1/players/me/compliance player token

Get the compliance view (bracket + gated map)

response
{
  "bracket": "13_15",
  "consent_state": "pending",
  "gated": {
    "open_chat": true
  }
}
GET /v1/compliance/policy publishable key

Get the game's gate policy (pre-token)

pk-only so the client can pre-disable regulated UI before a player token exists.

response
{
  "gates": {
    "open_chat": "13_15",
    "lootbox": "adult"
  },
  "coppa_mode": false,
  "default_jurisdiction": "US"
}
POST /v1/players/me/consent/request player token

Request parental consent (emails the parent)

request
{
  "parent_email": "parent@example.com"
}
response
{
  "id": "pc_1",
  "state": "pending",
  "expires_at": "2026-07-24T00:00:00Z"
}
GET /v1/players/me/consent player token

Get the player's consent record

response
{
  "consent": {
    "id": "pc_1",
    "state": "pending"
  }
}
GET /v1/consent/:token no auth

Parent consent landing page (HTML)

The signed link a parent receives by email; renders Approve/Decline. No pk/token — the signed token IS the credential.

  • token (path) — Signed consent token from the email.
POST /v1/consent/:token/decide no auth

Record the parent's decision

  • token (path) — Signed consent token.
request
{
  "grant": true,
  "note": "optional"
}
response
{
  "state": "granted"
}

Crashes

POST /v1/crashes player token

Report a crash

Grouped server-side by a normalized-stack fingerprint into a handful of issues with a crash-free-users %.

request
{
  "message": "TypeError: x is undefined",
  "stack": "at play (game.js:42)",
  "platform": "web",
  "appVersion": "1.4.0"
}
response
{
  "ok": true,
  "group_id": "cg_1"
}

RNG

GET /v1/rng/:stream player token

Deterministic server-seeded random values

A verifiable per-player, per-period stream — the same request returns the same values, so loot/crit rolls can't be client-forged.

  • stream (path) — Stream name (e.g. 'loot').
  • count (query) — How many values.
response
{
  "stream": "loot",
  "period_key": "2026-07",
  "values": [
    0.42,
    0.88
  ]
}

Realtime

GET /v1/realtime/rooms/:room player token

Join a realtime room (WebSocket upgrade)

A WebSocket upgrade. Because a browser WS can't send headers, the pk + player token go in the query (?key=&token=). Presence + broadcast; chat is moderated in transit. Rooms named team:<id> / match:<id> require membership. Use tg.realtime.join(room) from the SDK.

  • room (path) — Room name; team:/match: prefixes enforce membership.
  • key (query) — Publishable key (query, since a WS can't set headers).
  • token (query) — Player token.

Economy

GET /v1/wallet player token

Get all currency balances

response
{
  "balances": [
    {
      "currency": "gold",
      "amount": 250
    }
  ]
}
GET /v1/wallet/history player token

Get currency transaction history

  • limit (query) — Max lines.
response
{
  "lines": [
    {
      "currency": "gold",
      "delta": -100,
      "reason": "store_buy",
      "created_at": "2026-07-01T00:00:00Z"
    }
  ]
}
GET /v1/wallet/:currency player token

Get one currency's balance

  • currency (path) — Currency code.
response
{
  "currency": "gold",
  "amount": 250
}
GET /v1/stores player token

List stores

response
{
  "stores": [
    {
      "key": "main_store",
      "name": "Shop"
    }
  ]
}
GET /v1/stores/:key player token

Get a store's listings

  • key (path) — Store key.
response
{
  "key": "main_store",
  "listings": [
    {
      "id": "l_1",
      "item": "sword",
      "price": {
        "gold": 100
      }
    }
  ]
}
POST /v1/stores/:key/buy player token

Buy a store listing

Server-authoritative price + balance check; idempotent (pass idem). Fails with insufficient_funds / out_of_stock / store_limit_reached.

  • key (path) — Store key.
request
{
  "listing_id": "l_1",
  "idem": "uuid"
}
response
{
  "ok": true,
  "granted": {
    "items": [
      {
        "item": "sword",
        "qty": 1
      }
    ]
  }
}
GET /v1/inventory player token

List the player's inventory

response
{
  "items": [
    {
      "item": "sword",
      "qty": 1,
      "equipped": true
    }
  ]
}
POST /v1/inventory/:item/consume player token

Consume an item

  • item (path) — Item id.
request
{
  "qty": 1,
  "idem": "uuid"
}
response
{
  "ok": true,
  "remaining": 0
}
POST /v1/inventory/:item/equip player token

Equip an item

  • item (path) — Item id.
response
{
  "ok": true,
  "equipped": true
}
POST /v1/inventory/:item/unequip player token

Unequip an item

  • item (path) — Item id.
response
{
  "ok": true,
  "equipped": false
}
GET /v1/loot/:key/odds player token

Get a loot box's disclosed odds

  • key (path) — Loot box key.
response
{
  "key": "bronze_box",
  "odds": [
    {
      "item": "common",
      "weight": 0.9
    },
    {
      "item": "rare",
      "weight": 0.1
    }
  ]
}
POST /v1/loot/:key/open player token

Open a loot box

Server-rolled (verifiable RNG), idempotent. loot_not_enabled if the box isn't configured; age-gated where required.

  • key (path) — Loot box key.
request
{
  "idem": "uuid"
}
response
{
  "rolled": [
    {
      "item": "rare",
      "qty": 1
    }
  ]
}
GET /v1/energy player token

List energy meters

response
{
  "meters": [
    {
      "meter": "stamina",
      "current": 4,
      "max": 5,
      "refill_at": "2026-07-10T01:00:00Z"
    }
  ]
}
GET /v1/energy/:meter player token

Get one energy meter

  • meter (path) — Meter name.
response
{
  "meter": "stamina",
  "current": 4,
  "max": 5
}
POST /v1/energy/:meter/spend player token

Spend energy

out_of_energy if the balance is insufficient. Idempotent.

  • meter (path) — Meter name.
request
{
  "amount": 1,
  "idem": "uuid"
}
response
{
  "meter": "stamina",
  "current": 3
}
POST /v1/energy/:meter/refill player token

Refill energy (e.g. with a currency)

  • meter (path) — Meter name.
request
{
  "idem": "uuid"
}
response
{
  "meter": "stamina",
  "current": 5
}
POST /v1/codes/redeem player token

Redeem a promo code

Grants the campaign reward into the inbox. code_invalid / code_expired / code_already_redeemed / code_campaign_exhausted on failure.

request
{
  "code": "LAUNCH2026"
}
response
{
  "ok": true,
  "granted": {
    "stats": [
      {
        "key": "gold",
        "amount": 100
      }
    ]
  }
}
POST /v1/gifts player token

Send a gift to another player

Delivers an item/currency gift into the recipient's inbox (subject to gifting limits).

request
{
  "to": "p_9",
  "item": "sword",
  "qty": 1,
  "idem": "uuid"
}
response
{
  "ok": true
}

Teams

POST /v1/teams player token

Create a team

request
{
  "name": "Alpha Squad",
  "tag": "ALPHA",
  "privacy": "open"
}
response
{
  "id": "tm_1",
  "name": "Alpha Squad",
  "tag": "ALPHA"
}
GET /v1/teams player token

List / search teams

  • q (query) — Search query.
response
{
  "teams": [
    {
      "id": "tm_1",
      "name": "Alpha Squad",
      "member_count": 4
    }
  ]
}
GET /v1/teams/mine player token

Get the caller's team

response
{
  "team": {
    "id": "tm_1",
    "role": "owner"
  }
}
GET /v1/teams/leaderboards/:board player token

Team leaderboard

  • board (path) — Board key.
response
{
  "entries": [
    {
      "team_id": "tm_1",
      "score": 5000
    }
  ]
}
GET /v1/teams/:id player token

Get a team + members

  • id (path) — Team id.
response
{
  "id": "tm_1",
  "name": "Alpha Squad",
  "members": [
    {
      "player_id": "p_1",
      "role": "owner"
    }
  ]
}
POST /v1/teams/:id/join player token

Join a team (open teams)

  • id (path) — Team id.
response
{
  "ok": true,
  "role": "member"
}
POST /v1/teams/:id/leave player token

Leave a team

  • id (path) — Team id.
response
{
  "ok": true
}
POST /v1/teams/:id/members/:pid/kick player token

Kick a member (admin/owner)

  • id (path) — Team id.
  • pid (path) — Member player id.
response
{
  "ok": true
}
POST /v1/teams/:id/members/:pid/role player token

Change a member's role

  • id (path) — Team id.
  • pid (path) — Member player id.
request
{
  "role": "admin"
}
response
{
  "ok": true
}
POST /v1/teams/:id/transfer player token

Transfer ownership

  • id (path) — Team id.
request
{
  "to": "p_2"
}
response
{
  "ok": true
}
POST /v1/teams/:id/disband player token

Disband a team (owner)

  • id (path) — Team id.
response
{
  "ok": true
}
GET /v1/teams/mine/invites player token

List the caller's team invites

response
{
  "invites": [
    {
      "id": "ti_1",
      "team_id": "tm_1"
    }
  ]
}
POST /v1/teams/:id/invites player token

Invite a player

  • id (path) — Team id.
request
{
  "player_id": "p_7"
}
response
{
  "id": "ti_1",
  "state": "pending"
}
POST /v1/teams/invites/:iid/accept player token

Accept an invite

  • iid (path) — Invite id.
response
{
  "ok": true
}
POST /v1/teams/invites/:iid/reject player token

Reject an invite

  • iid (path) — Invite id.
response
{
  "ok": true
}
POST /v1/teams/:id/requests player token

Request to join (closed teams)

  • id (path) — Team id.
response
{
  "id": "tr_1",
  "state": "pending"
}
GET /v1/teams/:id/requests player token

List join requests (admin)

  • id (path) — Team id.
response
{
  "requests": [
    {
      "id": "tr_1",
      "player_id": "p_7"
    }
  ]
}
POST /v1/teams/:id/requests/:rid/approve player token

Approve a join request

  • id (path) — Team id.
  • rid (path) — Request id.
response
{
  "ok": true
}
POST /v1/teams/:id/requests/:rid/reject player token

Reject a join request

  • id (path) — Team id.
  • rid (path) — Request id.
response
{
  "ok": true
}
POST /v1/teams/:id/members/:pid/ban player token

Ban a member from the team

  • id (path) — Team id.
  • pid (path) — Player id.
response
{
  "ok": true
}
POST /v1/teams/:id/members/:pid/unban player token

Unban a player

  • id (path) — Team id.
  • pid (path) — Player id.
response
{
  "ok": true
}
GET /v1/teams/:id/bans player token

List team bans

  • id (path) — Team id.
response
{
  "bans": [
    {
      "player_id": "p_9"
    }
  ]
}

Competition

GET /v1/tournaments player token

List tournaments

response
{
  "tournaments": [
    {
      "id": "to_1",
      "title": "Weekend Cup",
      "state": "live"
    }
  ]
}
GET /v1/tournaments/mine player token

Tournaments the player has joined

response
{
  "tournaments": []
}
GET /v1/tournaments/:id player token

Get a tournament

  • id (path) — Tournament id.
response
{
  "id": "to_1",
  "title": "Weekend Cup",
  "starts_at": "2026-07-12T00:00:00Z"
}
GET /v1/tournaments/:id/standings player token

Get tournament standings

  • id (path) — Tournament id.
response
{
  "entries": [
    {
      "rank": 1,
      "player_id": "p_9",
      "score": 12000
    }
  ]
}
POST /v1/tournaments/:id/join player token

Join a tournament

tournament_not_open if entry is closed; may require an entry fee.

  • id (path) — Tournament id.
response
{
  "ok": true
}
GET /v1/tournaments/:id/me player token

The player's tournament entry

  • id (path) — Tournament id.
response
{
  "rank": 42,
  "score": 8000
}
POST /v1/leagues/:key/join player token

Join a league

  • key (path) — League key.
response
{
  "division": "bronze",
  "tier": 3
}
GET /v1/leagues/:key/me player token

The player's league standing

  • key (path) — League key.
response
{
  "division": "bronze",
  "tier": 3,
  "rank": 5
}
GET /v1/leagues/:key/divisions/:tier/top player token

Top of a league division

  • key (path) — League key.
  • tier (path) — Division tier.
response
{
  "entries": [
    {
      "rank": 1,
      "player_id": "p_9"
    }
  ]
}
POST /v1/boards/:board/submit player token

Submit to a keyed board (team / UGC / custom entity)

  • board (path) — Keyed board key.
request
{
  "entity_id": "tm_1",
  "score": 5000
}
response
{
  "ok": true,
  "best_score": 5000
}
GET /v1/boards/:board/top player token

Top entries of a keyed board

  • board (path) — Keyed board key.
response
{
  "entries": [
    {
      "rank": 1,
      "entity_id": "tm_1",
      "score": 5000
    }
  ]
}
GET /v1/boards/:board/entries/:entity_id player token

One entity's keyed-board entry

  • board (path) — Keyed board key.
  • entity_id (path) — Entity id (team/UGC/…).
response
{
  "entity_id": "tm_1",
  "score": 5000,
  "rank": 1
}

Progression

GET /v1/quests player token

List quests + the caller's progress

response
{
  "quests": [
    {
      "key": "daily_login",
      "progress": 1,
      "target": 1,
      "claimable": true
    }
  ]
}
POST /v1/quests/:key/claim player token

Claim a completed quest's reward

quest_not_complete if objectives aren't met; the reward is escrowed into the inbox.

  • key (path) — Quest key.
response
{
  "claimed": true
}
GET /v1/battle-pass/:season player token

Get battle-pass tiers + the caller's progress

  • season (path) — Season key.
response
{
  "season": "s1",
  "tier": 4,
  "xp": 1200,
  "premium": false
}
POST /v1/battle-pass/:season/claim player token

Claim a battle-pass tier reward

tier_not_earned if not reached; premium_required for a premium lane without the pass.

  • season (path) — Season key.
request
{
  "tier": 4,
  "lane": "free"
}
response
{
  "claimed": true
}
GET /v1/progression player token

Get XP / level curve progress

response
{
  "level": 7,
  "xp": 3400,
  "next_level_xp": 4000
}

Storage

GET /v1/storage/:collection player token

List keys in the player's collection

  • collection (path) — Collection key.
response
{
  "keys": [
    "settings",
    "deck"
  ]
}
GET /v1/storage/:collection/:key player token

Read a collection entry

  • collection (path) — Collection key.
  • key (path) — Entry key.
response
{
  "key": "settings",
  "data": {
    "sfx": true
  },
  "version": 2
}
PUT /v1/storage/:collection/:key player token

Write a collection entry

Optional If-Match for OCC (storage_conflict on a stale version).

  • collection (path) — Collection key.
  • key (path) — Entry key.
request
{
  "data": {
    "sfx": false
  }
}
response
{
  "key": "settings",
  "version": 3
}
DELETE /v1/storage/:collection/:key player token

Delete a collection entry

  • collection (path) — Collection key.
  • key (path) — Entry key.
response · 204 No Content.
null
POST /v1/storage/:collection/:key/mutate player token

Atomic server-side mutation of an entry

Apply structured ops (e.g. list append, counter add) server-side to avoid read-modify-write races.

  • collection (path) — Collection key.
  • key (path) — Entry key.
request
{
  "ops": [
    {
      "op": "append",
      "path": "deck",
      "value": "card_9"
    }
  ]
}
response
{
  "key": "deck",
  "version": 5
}
GET /v1/storage/shared/:collection/:key player token

Read a shared (game-wide) entry

  • collection (path) — Collection key.
  • key (path) — Entry key.
response
{
  "key": "motd",
  "data": {
    "text": "Welcome!"
  }
}
PUT /v1/storage/shared/:collection/:key player token

Write a shared entry (per the collection's write policy)

  • collection (path) — Collection key.
  • key (path) — Entry key.
request
{
  "data": {
    "text": "Welcome!"
  }
}
response
{
  "key": "motd",
  "version": 1
}
GET /v1/storage/team/:teamId/:collection player token

List a team collection's keys (members only)

  • teamId (path) — Team id.
  • collection (path) — Collection key.
response
{
  "keys": [
    "roster"
  ]
}
GET /v1/storage/team/:teamId/:collection/:key player token

Read a team entry

  • teamId (path) — Team id.
  • collection (path) — Collection key.
  • key (path) — Entry key.
response
{
  "key": "roster",
  "data": {}
}
PUT /v1/storage/team/:teamId/:collection/:key player token

Write a team entry (members only)

  • teamId (path) — Team id.
  • collection (path) — Collection key.
  • key (path) — Entry key.
request
{
  "data": {}
}
response
{
  "key": "roster",
  "version": 1
}
DELETE /v1/storage/team/:teamId/:collection/:key player token

Delete a team entry

  • teamId (path) — Team id.
  • collection (path) — Collection key.
  • key (path) — Entry key.
response · 204 No Content.
null

UGC

GET /v1/ugc player token

Browse published content

  • sort (query) — e.g. top, new.
response
{
  "items": [
    {
      "id": "ug_1",
      "title": "My Level",
      "plays": 42,
      "rating": 4.5
    }
  ]
}
GET /v1/ugc/mine player token

List the caller's content

response
{
  "items": [
    {
      "id": "ug_1",
      "title": "My Level",
      "status": "published"
    }
  ]
}
POST /v1/ugc player token

Create a draft

Title/content are moderated. Publish it with POST /v1/ugc/:id/submit.

request
{
  "title": "My Level",
  "data": {
    "grid": []
  }
}
response
{
  "id": "ug_1",
  "status": "draft"
}
GET /v1/ugc/:id player token

Get a content item

  • id (path) — Content id.
response
{
  "id": "ug_1",
  "title": "My Level",
  "data": {},
  "author": "p_1"
}
PATCH /v1/ugc/:id player token

Update a draft

  • id (path) — Content id.
request
{
  "title": "My Level v2"
}
response
{
  "id": "ug_1"
}
DELETE /v1/ugc/:id player token

Delete content

  • id (path) — Content id.
response · 204 No Content.
null
POST /v1/ugc/:id/submit player token

Publish a draft

  • id (path) — Content id.
response
{
  "id": "ug_1",
  "status": "published"
}
POST /v1/ugc/:id/play player token

Record a play

  • id (path) — Content id.
response
{
  "ok": true
}
GET /v1/ugc/:id/lineage player token

Get remix lineage (attribution)

  • id (path) — Content id.
response
{
  "ancestors": [
    {
      "id": "ug_0",
      "author": "p_0"
    }
  ]
}
POST /v1/ugc/:id/remix player token

Remix content (forks with attribution)

  • id (path) — Source content id.
request
{
  "title": "My Remix"
}
response
{
  "id": "ug_2",
  "remixed_from": "ug_1"
}
POST /v1/ugc/:id/rate player token

Rate content

  • id (path) — Content id.
request
{
  "rating": 5
}
response
{
  "ok": true,
  "rating": 4.6
}
POST /v1/ugc/:id/like player token

Like content

  • id (path) — Content id.
response
{
  "ok": true,
  "likes": 13
}
DELETE /v1/ugc/:id/like player token

Unlike content

  • id (path) — Content id.
response
{
  "ok": true,
  "likes": 12
}

Async

POST /v1/async player token

Create a turn-based match

No realtime/Durable Object needed — turns are submitted over HTTP with server-enforced turn order + OCC.

request
{
  "type": "chess",
  "opponents": [
    "p_9"
  ]
}
response
{
  "match": {
    "id": "am_1",
    "state": {},
    "turn": "p_1a2b3c"
  }
}
GET /v1/async/mine player token

List the player's active matches

response
{
  "matches": [
    {
      "id": "am_1",
      "type": "chess",
      "turn": "p_9"
    }
  ]
}
GET /v1/async/:id player token

Get a match (participants only)

  • id (path) — Match id.
response
{
  "match": {
    "id": "am_1",
    "state": {},
    "turn_number": 4
  }
}
POST /v1/async/:id/turn player token

Submit a turn

Rejected with not_your_turn out of order, or async_conflict on a stale version (send the version you read).

  • id (path) — Match id.
request
{
  "state": {
    "board": []
  },
  "version": 4,
  "end": false
}
response
{
  "match": {
    "id": "am_1",
    "turn_number": 5
  }
}
POST /v1/async/:id/forfeit player token

Forfeit a match

  • id (path) — Match id.
response
{
  "match": {
    "id": "am_1",
    "status": "complete",
    "winner": "p_9"
  }
}

LiveOps

GET /v1/config publishable key

Get the game's published config blob

The developer's key→value tuning blob (server-controlled, no client deploy to change).

response
{
  "config": {
    "spawn_rate": 1.5,
    "event_banner": "summer"
  },
  "version": 12
}
GET /v1/flags player token

Get feature flags resolved for the player

Each flag resolves per the player's segments, with a break-glass kill switch honoured.

response
{
  "flags": {
    "new_hud": true,
    "checkout_v2": "variant_b"
  }
}
GET /v1/flags/:key player token

Get one resolved flag value

  • key (path) — Flag key.
response
{
  "key": "new_hud",
  "value": true
}
GET /v1/liveops/events/live player token

Get currently-live events

response
{
  "events": [
    {
      "key": "summer_fest",
      "ends_at": "2026-08-01T00:00:00Z"
    }
  ]
}
GET /v1/players/me/segments player token

Get the player's segment memberships

response
{
  "segments": [
    "whales",
    "new_players"
  ]
}

Analytics

POST /v1/events player token

Ingest analytics events

Usually called via the SDK's durable outbox (tg.track), which coalesces + retries. Counts only — no per-player PII.

request
{
  "events": [
    {
      "name": "level_complete",
      "count": 1
    }
  ]
}
response
{
  "accepted": 1
}

Dev · Games

POST /v1/dev/games developer session

Create a game

request
{
  "name": "Neon Drift"
}
response
{
  "id": "g_1",
  "name": "Neon Drift",
  "tier": "shared",
  "env": "prod"
}
GET /v1/dev/games developer session

List your games

response
{
  "games": [
    {
      "id": "g_1",
      "name": "Neon Drift",
      "status": "active"
    }
  ]
}
GET /v1/dev/games/:id developer session

Get a game

  • id (path) — Game id.
response
{
  "id": "g_1",
  "name": "Neon Drift",
  "allowed_origins": [
    "https://mygame.com"
  ]
}
PATCH /v1/dev/games/:id developer session

Update a game (name, CORS allowlist, pause)

Set allowed_origins (CORS) — an empty list is open to any origin. status paused takes the game offline.

  • id (path) — Game id.
request
{
  "allowed_origins": [
    "https://mygame.com"
  ],
  "status": "active"
}
response
{
  "id": "g_1",
  "allowed_origins": [
    "https://mygame.com"
  ]
}
DELETE /v1/dev/games/:id developer session

Delete a game (and its keys/data)

  • id (path) — Game id.
response · 204 No Content.
null
GET /v1/dev/me developer session

Get the developer account

response
{
  "id": "d_1",
  "email": "dev@studio.com"
}

Dev · Keys

POST /v1/dev/games/:id/keys developer session

Issue an API key

The full secret is returned ONCE. tg_pk_ (publishable) is safe in clients; tg_sk_ (secret) is server-only.

  • id (path) — Game id.
request
{
  "kind": "publishable"
}
response
{
  "id": "key_1",
  "prefix": "tg_pk_ab12",
  "key": "tg_pk_ab12…full-shown-once",
  "note": "Store this now — it won't be shown again."
}
GET /v1/dev/games/:id/keys developer session

List key metadata (never the secret)

  • id (path) — Game id.
response
{
  "keys": [
    {
      "id": "key_1",
      "kind": "publishable",
      "prefix": "tg_pk_ab12",
      "last_used_at": "2026-07-01T00:00:00Z",
      "revoked_at": null
    }
  ]
}
POST /v1/dev/games/:id/keys/:keyId/rotate developer session

Rotate a key (revoke + mint a replacement)

  • id (path) — Game id.
  • keyId (path) — Key id.
response
{
  "id": "key_2",
  "key": "tg_pk_cd34…full-shown-once"
}
DELETE /v1/dev/games/:id/keys/:keyId developer session

Revoke a key

  • id (path) — Game id.
  • keyId (path) — Key id.
response · 204 No Content.
null

Dev · Leaderboards

GET /v1/dev/games/:id/leaderboards developer session

List leaderboard definitions

  • id (path) — Game id.
response
{
  "boards": [
    {
      "board": "high_scores",
      "aggregation": "best",
      "period": "all_time"
    }
  ]
}
PUT /v1/dev/games/:id/leaderboards/:board developer session

Create / update a leaderboard

  • id (path) — Game id.
  • board (path) — Board key.
request
{
  "aggregation": "best",
  "period": "weekly",
  "higher_is_better": true
}
response
{
  "board": "high_scores"
}
DELETE /v1/dev/games/:id/leaderboards/:board developer session

Delete a leaderboard (scores cascade)

  • id (path) — Game id.
  • board (path) — Board key.
response · 204 No Content.
null
POST /v1/dev/games/:id/boards developer session

Create a keyed board (team/UGC/custom entity)

  • id (path) — Game id.
request
{
  "board_key": "team_wars",
  "entity_type": "team",
  "aggregation": "sum",
  "period": "weekly"
}
response
{
  "board_key": "team_wars"
}
GET /v1/dev/games/:id/boards developer session

List keyed boards

  • id (path) — Game id.
response
{
  "boards": [
    {
      "board_key": "team_wars",
      "entity_type": "team"
    }
  ]
}
DELETE /v1/dev/games/:id/boards/:board developer session

Delete a keyed board

  • id (path) — Game id.
  • board (path) — Keyed board key.
response · 204 No Content.
null

Dev · Economy

GET /v1/dev/games/:id/economy/currencies developer session

List currencies

  • id (path) — Game id.
response
{
  "currencies": [
    {
      "code": "gold",
      "name": "Gold"
    }
  ]
}
POST /v1/dev/games/:id/economy/items developer session

Create / upsert an item

  • id (path) — Game id.
request
{
  "key": "sword",
  "name": "Sword",
  "stackable": false
}
response
{
  "key": "sword"
}
GET /v1/dev/games/:id/economy/items developer session

List items

  • id (path) — Game id.
response
{
  "items": [
    {
      "key": "sword"
    }
  ]
}
POST /v1/dev/games/:id/economy/stores developer session

Create / upsert a store

  • id (path) — Game id.
request
{
  "key": "main_store",
  "listings": [
    {
      "id": "l_1",
      "item": "sword",
      "price": {
        "gold": 100
      }
    }
  ]
}
response
{
  "key": "main_store"
}
GET /v1/dev/games/:id/economy/stores developer session

List stores

  • id (path) — Game id.
response
{
  "stores": [
    {
      "key": "main_store"
    }
  ]
}
POST /v1/dev/games/:id/economy/loot developer session

Create / upsert a loot box (with odds)

  • id (path) — Game id.
request
{
  "key": "bronze_box",
  "drops": [
    {
      "item": "common",
      "weight": 0.9
    },
    {
      "item": "rare",
      "weight": 0.1
    }
  ]
}
response
{
  "key": "bronze_box"
}
GET /v1/dev/games/:id/economy/loot developer session

List loot boxes

  • id (path) — Game id.
response
{
  "loot": [
    {
      "key": "bronze_box"
    }
  ]
}
POST /v1/dev/games/:id/economy/energy developer session

Create / upsert an energy meter

  • id (path) — Game id.
request
{
  "meter": "stamina",
  "max": 5,
  "refill_seconds": 600
}
response
{
  "meter": "stamina"
}
GET /v1/dev/games/:id/economy/energy developer session

List energy meters

  • id (path) — Game id.
response
{
  "meters": [
    {
      "meter": "stamina",
      "max": 5
    }
  ]
}
POST /v1/dev/games/:id/economy/grant developer session

Grant currency/items to a player (support tool)

Escrows the grant into the player's inbox (the one hardened grant path). Idempotent.

  • id (path) — Game id.
request
{
  "player_id": "p_9",
  "currency": [
    {
      "code": "gold",
      "amount": 500
    }
  ],
  "idem": "uuid"
}
response
{
  "ok": true
}
GET /v1/dev/games/:id/economy/ledger developer session

Read the currency ledger

  • id (path) — Game id.
response
{
  "lines": [
    {
      "player_id": "p_9",
      "currency": "gold",
      "delta": 500
    }
  ]
}
GET /v1/dev/games/:id/economy/players/:pid/wallet developer session

Inspect a player's wallet

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "balances": [
    {
      "currency": "gold",
      "amount": 500
    }
  ]
}
GET /v1/dev/games/:id/economy/players/:pid/inventory developer session

Inspect a player's inventory

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "items": [
    {
      "item": "sword",
      "qty": 1
    }
  ]
}
POST /v1/dev/games/:id/economy/currencies developer session

Create / upsert a currency

  • id (path) — Game id.
request
{
  "code": "gold",
  "name": "Gold",
  "max": 999999
}
response
{
  "code": "gold"
}

Dev · Progression

GET /v1/dev/games/:id/achievements developer session

List achievement definitions

  • id (path) — Game id.
response
{
  "achievements": [
    {
      "key": "first_win",
      "target": 1
    }
  ]
}
DELETE /v1/dev/games/:id/achievements/:key developer session

Delete an achievement

  • id (path) — Game id.
  • key (path) — Achievement key.
response · 204 No Content.
null
GET /v1/dev/games/:id/daily developer session

Get the daily-reward config

  • id (path) — Game id.
response
{
  "cycle_length": 7,
  "enabled": true
}
PUT /v1/dev/games/:id/daily developer session

Set the daily-reward config

  • id (path) — Game id.
request
{
  "cycle_length": 7,
  "rewards": [
    {
      "day": 1,
      "stats": [
        {
          "key": "gold",
          "amount": 50
        }
      ]
    }
  ]
}
response
{
  "enabled": true
}
DELETE /v1/dev/games/:id/daily developer session

Disable daily rewards

  • id (path) — Game id.
response · 204 No Content.
null
POST /v1/dev/games/:id/quests developer session

Create / upsert a quest

  • id (path) — Game id.
request
{
  "key": "win_3",
  "period": "weekly",
  "objectives": [
    {
      "stat": "wins",
      "target": 3
    }
  ]
}
response
{
  "key": "win_3"
}
GET /v1/dev/games/:id/quests developer session

List quests

  • id (path) — Game id.
response
{
  "quests": [
    {
      "key": "win_3"
    }
  ]
}
POST /v1/dev/games/:id/battle-pass developer session

Create / upsert a battle-pass season

  • id (path) — Game id.
request
{
  "season": "s1",
  "starts_at": "2026-07-01T00:00:00Z",
  "tiers": []
}
response
{
  "season": "s1"
}
GET /v1/dev/games/:id/battle-pass developer session

List battle-pass seasons

  • id (path) — Game id.
response
{
  "seasons": [
    {
      "season": "s1"
    }
  ]
}
POST /v1/dev/games/:id/progression developer session

Set the XP / level curve

  • id (path) — Game id.
request
{
  "xp_key": "xp",
  "base": 100,
  "growth": 1.5,
  "max_level": 50
}
response
{
  "max_level": 50
}
GET /v1/dev/games/:id/progression developer session

Get the level curve

  • id (path) — Game id.
response
{
  "base": 100,
  "growth": 1.5,
  "max_level": 50
}
PUT /v1/dev/games/:id/achievements/:key developer session

Create / update an achievement

  • id (path) — Game id.
  • key (path) — Achievement key.
request
{
  "name": "First Win",
  "target": 1,
  "reward": {
    "stats": [
      {
        "key": "gold",
        "amount": 50
      }
    ]
  },
  "secret": false
}
response
{
  "key": "first_win"
}
POST /v1/dev/games/:id/battle-pass/:season/premium developer session

Configure a season's premium track

  • id (path) — Game id.
  • season (path) — Season key.
request
{
  "price": {
    "gold": 1000
  }
}
response
{
  "ok": true
}

Dev · Competition

POST /v1/dev/games/:id/tournaments developer session

Schedule a tournament

  • id (path) — Game id.
request
{
  "key": "weekend_cup",
  "board": "high_scores",
  "starts_at": "2026-07-12T00:00:00Z",
  "ends_at": "2026-07-14T00:00:00Z",
  "reward_table": []
}
response
{
  "id": "to_1"
}
GET /v1/dev/games/:id/tournaments developer session

List tournaments

  • id (path) — Game id.
response
{
  "tournaments": [
    {
      "id": "to_1",
      "state": "live"
    }
  ]
}
POST /v1/dev/games/:id/tournaments/:tid/finalize developer session

Force-finalize a tournament (pays prizes)

  • id (path) — Game id.
  • tid (path) — Tournament id.
response
{
  "finalized": true
}
POST /v1/dev/games/:id/leagues developer session

Create / upsert a league (tiered divisions)

  • id (path) — Game id.
request
{
  "key": "ranked",
  "divisions": [
    "bronze",
    "silver",
    "gold"
  ]
}
response
{
  "key": "ranked"
}
GET /v1/dev/games/:id/leagues developer session

List leagues

  • id (path) — Game id.
response
{
  "leagues": [
    {
      "key": "ranked"
    }
  ]
}
POST /v1/dev/games/:id/leagues/:key/advance developer session

Advance a league season (promotion/relegation)

  • id (path) — Game id.
  • key (path) — League key.
response
{
  "advanced": true,
  "season": 2
}

Dev · LiveOps

GET /v1/dev/games/:id/config developer session

Get the config blob

  • id (path) — Game id.
response
{
  "config": {},
  "version": 12
}
PUT /v1/dev/games/:id/config developer session

Replace the config blob

Whole-blob replace (≤64 KB); bumps config_version. Served to clients at GET /v1/config.

  • id (path) — Game id.
request
{
  "config": {
    "spawn_rate": 1.5
  }
}
response
{
  "version": 13
}
GET /v1/dev/games/:id/events developer session

Read remote-event counters

  • id (path) — Game id.
response
{
  "events": [
    {
      "name": "level_complete",
      "count": 1240
    }
  ]
}
POST /v1/dev/games/:id/liveops/flags developer session

Create / upsert a feature flag

  • id (path) — Game id.
request
{
  "key": "new_hud",
  "type": "boolean",
  "default_value": true,
  "safe_value": false
}
response
{
  "key": "new_hud",
  "state": "on"
}
GET /v1/dev/games/:id/liveops/flags developer session

List feature flags

  • id (path) — Game id.
response
{
  "flags": [
    {
      "key": "new_hud",
      "state": "on"
    }
  ]
}
POST /v1/dev/games/:id/liveops/flags/:key/kill developer session

Break-glass: kill / restore a flag

  • id (path) — Game id.
  • key (path) — Flag key.
request
{
  "on": true
}
response
{
  "key": "new_hud",
  "state": "killed"
}
POST /v1/dev/games/:id/liveops/segments developer session

Create / upsert a targeting segment

  • id (path) — Game id.
request
{
  "key": "whales",
  "definition": {
    "all": [
      {
        "stat": "coins",
        "op": ">=",
        "value": 1000
      }
    ]
  }
}
response
{
  "id": "sg_1",
  "key": "whales"
}
GET /v1/dev/games/:id/liveops/segments developer session

List segments

  • id (path) — Game id.
response
{
  "segments": [
    {
      "id": "sg_1",
      "key": "whales",
      "approx_size": 1200
    }
  ]
}
GET /v1/dev/games/:id/liveops/segments/:sid developer session

Get a segment's rule definition

  • id (path) — Game id.
  • sid (path) — Segment id.
response
{
  "definition": {
    "all": [
      {
        "stat": "coins",
        "op": ">=",
        "value": 1000
      }
    ]
  }
}
POST /v1/dev/games/:id/liveops/segments/:sid/materialize developer session

Materialize a segment's membership

  • id (path) — Game id.
  • sid (path) — Segment id.
response
{
  "size": 1200
}
POST /v1/dev/games/:id/liveops/code-campaigns developer session

Create a promo-code campaign

  • id (path) — Game id.
request
{
  "key": "launch",
  "reward": {
    "stats": [
      {
        "key": "gold",
        "amount": 100
      }
    ]
  },
  "max_redemptions": 1000
}
response
{
  "key": "launch"
}
GET /v1/dev/games/:id/liveops/code-campaigns developer session

List code campaigns

  • id (path) — Game id.
response
{
  "campaigns": [
    {
      "key": "launch",
      "redeemed": 42
    }
  ]
}
GET /v1/dev/games/:id/liveops/code-campaigns/:cid/redemptions developer session

List a campaign's redemptions

  • id (path) — Game id.
  • cid (path) — Campaign id.
response
{
  "redemptions": [
    {
      "player_id": "p_9",
      "at": "2026-07-01T00:00:00Z"
    }
  ]
}
POST /v1/dev/games/:id/liveops/events developer session

Create / upsert a live event

  • id (path) — Game id.
request
{
  "key": "summer_fest",
  "starts_at": "2026-07-01T00:00:00Z",
  "ends_at": "2026-08-01T00:00:00Z"
}
response
{
  "key": "summer_fest"
}
GET /v1/dev/games/:id/liveops/events developer session

List live events

  • id (path) — Game id.
response
{
  "events": [
    {
      "key": "summer_fest"
    }
  ]
}
POST /v1/dev/games/:id/liveops/code-campaigns/:cid/generate developer session

Generate promo codes for a campaign

  • id (path) — Game id.
  • cid (path) — Campaign id.
request
{
  "count": 100
}
response
{
  "codes": [
    "ABCD-1234"
  ]
}
POST /v1/dev/games/:id/liveops/segments/preview developer session

Preview a segment's size (dry-run)

  • id (path) — Game id.
request
{
  "definition": {
    "all": [
      {
        "stat": "coins",
        "op": ">=",
        "value": 1000
      }
    ]
  }
}
response
{
  "count": 1200
}

Dev · Moderation

POST /v1/dev/games/:id/moderation developer session

Set the moderation policy

  • id (path) — Game id.
request
{
  "custom_terms": [
    "voldemort"
  ],
  "allowlist": [
    "scunthorpe"
  ]
}
response
{
  "ok": true
}
GET /v1/dev/games/:id/moderation developer session

Get the moderation policy

  • id (path) — Game id.
response
{
  "customTerms": [
    "voldemort"
  ],
  "allowlist": [
    "scunthorpe"
  ]
}
POST /v1/dev/games/:id/moderation/check developer session

Dry-run moderate a string

  • id (path) — Game id.
request
{
  "surface": "chat",
  "text": "test a string"
}
response
{
  "verdict": "allow"
}
GET /v1/dev/games/:id/moderation/reports developer session

List content reports (review queue)

  • id (path) — Game id.
  • state (query) — Filter by state.
response
{
  "reports": [
    {
      "id": "cr_1",
      "state": "open",
      "target_type": "player"
    }
  ]
}
GET /v1/dev/games/:id/moderation/reports/:rp developer session

Get a report

  • id (path) — Game id.
  • rp (path) — Report id.
response
{
  "id": "cr_1",
  "state": "open"
}
POST /v1/dev/games/:id/moderation/reports/:rp/assign developer session

Assign / resolve a report

  • id (path) — Game id.
  • rp (path) — Report id.
request
{
  "resolution": "warn"
}
response
{
  "state": "actioned"
}
GET /v1/dev/games/:id/moderation/audit developer session

Read the moderation audit feed

  • id (path) — Game id.
response
{
  "actions": [
    {
      "action": "ban",
      "actor": "p_1",
      "target_id": "p_9"
    }
  ]
}
GET /v1/dev/games/:id/moderation/players/:pid developer session

Get a player's moderation view

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "player_id": "p_9",
  "reports_against": [],
  "actions": []
}
POST /v1/dev/games/:id/moderation/players/:pid/ban developer session

Ban a player

  • id (path) — Game id.
  • pid (path) — Player id.
request
{
  "kind": "permanent",
  "scope": "account",
  "reason": "cheating"
}
response
{
  "ok": true,
  "banned": true
}
POST /v1/dev/games/:id/moderation/players/:pid/unban developer session

Unban a player

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "ok": true,
  "lifted": true
}
POST /v1/dev/games/:id/moderation/players/:pid/unmute developer session

Unmute a player

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "ok": true,
  "lifted": true
}
GET /v1/dev/games/:id/moderation/appeals developer session

List ban appeals; decide grants/denies

  • id (path) — Game id.
response
{
  "appeals": [
    {
      "id": "ap_1",
      "state": "pending"
    }
  ]
}
POST /v1/dev/games/:id/moderation/players/:pid/mute developer session

Mute / restrict a player

  • id (path) — Game id.
  • pid (path) — Player id.
request
{
  "effect": "mute",
  "expires_at": "2026-07-15T00:00:00Z"
}
response
{
  "ok": true
}
POST /v1/dev/games/:id/moderation/reports/:rp/resolve developer session

Resolve a report (warn / dismiss / duplicate)

  • id (path) — Game id.
  • rp (path) — Report id.
request
{
  "resolution": "warn"
}
response
{
  "state": "actioned"
}
POST /v1/dev/games/:id/moderation/appeals/:ap/decide developer session

Decide a ban appeal (grant lifts the ban)

  • id (path) — Game id.
  • ap (path) — Appeal id.
request
{
  "grant": true,
  "note": "first offense"
}
response
{
  "appeal": {
    "id": "ap_1",
    "state": "granted"
  }
}

Dev · Compliance

POST /v1/dev/games/:id/compliance developer session

Set the age-gate / COPPA policy

  • id (path) — Game id.
request
{
  "coppa_mode": false,
  "default_jurisdiction": "US",
  "gates": {
    "open_chat": "13_15",
    "lootbox": "adult"
  }
}
response
{
  "ok": true
}
GET /v1/dev/games/:id/compliance developer session

Get the compliance policy

  • id (path) — Game id.
response
{
  "coppa_mode": false,
  "gates": {}
}
GET /v1/dev/games/:id/compliance/players/:pid developer session

Inspect a player's compliance state

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "bracket": "13_15",
  "consent_state": "granted"
}

Dev · Analytics

GET /v1/dev/games/:id/analytics/metrics developer session

DAU/MAU/new-players/events by day

  • id (path) — Game id.
response
{
  "days": [
    {
      "day": "2026-07-01",
      "dau": 1200,
      "mau": 8000,
      "new_players": 90
    }
  ]
}
GET /v1/dev/games/:id/analytics/retention developer session

Cohort retention grid

  • id (path) — Game id.
response
{
  "cohorts": [
    {
      "day": "2026-07-01",
      "d1": 0.42,
      "d7": 0.18
    }
  ]
}
GET /v1/dev/games/:id/analytics/sessions developer session

Session counts + lengths

  • id (path) — Game id.
response
{
  "days": [
    {
      "day": "2026-07-01",
      "sessions": 3400,
      "avg_ms": 480000
    }
  ]
}
GET /v1/dev/games/:id/analytics/economy developer session

Per-currency sources/sinks/net (economy health)

  • id (path) — Game id.
response
{
  "currencies": [
    {
      "currency": "gold",
      "sources": 150,
      "sinks": 30,
      "net": 120
    }
  ]
}
POST /v1/dev/games/:id/analytics/funnels developer session

Define a funnel (ordered event list)

  • id (path) — Game id.
request
{
  "key": "onboarding",
  "steps": [
    "install",
    "tutorial",
    "first_purchase"
  ]
}
response
{
  "key": "onboarding"
}
GET /v1/dev/games/:id/analytics/funnels developer session

List funnels + their results

  • id (path) — Game id.
response
{
  "funnels": [
    {
      "key": "onboarding",
      "steps": [
        {
          "event": "install",
          "reached": 1000
        }
      ]
    }
  ]
}
DELETE /v1/dev/games/:id/analytics/funnels/:key developer session

Delete a funnel

  • id (path) — Game id.
  • key (path) — Funnel key.
response · 204 No Content.
null
GET /v1/dev/games/:id/analytics/crashes developer session

List crash groups (open-first)

  • id (path) — Game id.
response
{
  "groups": [
    {
      "id": "cg_1",
      "title": "TypeError",
      "occurrences": 42,
      "status": "open"
    }
  ]
}
POST /v1/dev/games/:id/analytics/rollup developer session

Trigger an analytics rollup (manual)

  • id (path) — Game id.
response
{
  "ok": true
}
GET /v1/dev/games/:id/analytics/crashes/:group/daily developer session

A crash group's daily rate + crash-free %

  • id (path) — Game id.
  • group (path) — Crash group id.
response
{
  "days": [
    {
      "day": "2026-07-01",
      "occurrences": 12,
      "crash_free": 0.98
    }
  ]
}
POST /v1/dev/games/:id/analytics/crashes/:group/status developer session

Set a crash group's status (open/resolved/ignored)

  • id (path) — Game id.
  • group (path) — Crash group id.
request
{
  "status": "resolved"
}
response
{
  "ok": true
}
GET /v1/dev/games/:id/analytics/funnels/:key/results developer session

A funnel's per-step results

  • id (path) — Game id.
  • key (path) — Funnel key.
response
{
  "steps": [
    {
      "event": "install",
      "reached": 1000,
      "conversion": 1
    },
    {
      "event": "tutorial",
      "reached": 660,
      "conversion": 0.66
    }
  ]
}

Dev · Storage

POST /v1/dev/games/:id/storage-collections developer session

Create / upsert a storage collection

  • id (path) — Game id.
request
{
  "key": "decks",
  "scope": "player",
  "read": "owner",
  "write": "owner",
  "max_bytes": 65536
}
response
{
  "key": "decks"
}
GET /v1/dev/games/:id/storage-collections developer session

List storage collections

  • id (path) — Game id.
response
{
  "collections": [
    {
      "key": "decks",
      "scope": "player"
    }
  ]
}
PUT /v1/dev/games/:id/storage/shared/:collection/:key developer session

Write a shared storage entry (developer)

  • id (path) — Game id.
  • collection (path) — Collection key.
  • key (path) — Entry key.
request
{
  "data": {
    "motd": "Season 2 is live!"
  }
}
response
{
  "key": "motd",
  "version": 2
}

Dev · Inbox

POST /v1/dev/games/:id/inbox developer session

Send an inbox message / announcement (optionally with a reward)

  • id (path) — Game id.
request
{
  "audience": "all",
  "kind": "announcement",
  "body": {
    "text": "Season 2 is live!"
  },
  "rewards": {
    "stats": [
      {
        "key": "gold",
        "amount": 100
      }
    ]
  }
}
response
{
  "sent": 8000
}

Dev · Usage

GET /v1/dev/games/:id/usage developer session

Get this month's usage vs quota

  • id (path) — Game id.
response
{
  "month": "2026-07",
  "players": 8000,
  "ops": {
    "saves": 320,
    "leaderboards": 145
  }
}

Dev · Verify

POST /v1/dev/games/:id/verify developer session

Run a server-side integration self-test

Live-probes player → save → leaderboard → CORS in-process and reports pass/fail per service with fixes.

  • id (path) — Game id.
response
{
  "ok": true,
  "services": [
    {
      "name": "saves",
      "status": "pass"
    }
  ]
}

Admin

GET /v1/admin/me operator session

Get the signed-in operator

response
{
  "id": "op_1",
  "email": "ops@triggair.com",
  "role": "owner"
}
GET /v1/admin/developers operator session

List developers (cross-tenant)

  • limit (query) — Page size.
  • offset (query) — Offset.
response
{
  "developers": [
    {
      "id": "d_1",
      "email": "dev@x.com",
      "plan": "indie",
      "games": 2,
      "mau": 8000
    }
  ]
}
GET /v1/admin/developers/:id operator session

Get a developer (subscription + games)

  • id (path) — Developer id.
response
{
  "developer": {
    "id": "d_1",
    "email": "dev@x.com",
    "status": "active"
  },
  "subscription": {
    "plan": "indie"
  },
  "games": []
}
GET /v1/admin/games operator session

List games (cross-tenant)

response
{
  "games": [
    {
      "id": "g_1",
      "name": "Neon Drift",
      "developer_id": "d_1",
      "players": 42
    }
  ]
}
GET /v1/admin/games/:id operator session

Get a game (metadata + key metadata + counts)

Key metadata only — never a hash/secret. Every operator read is audited.

  • id (path) — Game id.
response
{
  "game": {
    "id": "g_1",
    "tier": "shared",
    "status": "active"
  },
  "keys": [
    {
      "prefix": "tg_pk_ab12",
      "kind": "publishable"
    }
  ],
  "players": 42,
  "ops": 525
}
GET /v1/admin/audit operator session

Read the operator audit trail

  • limit (query) — Page size.
response
{
  "entries": [
    {
      "action": "developer.read",
      "actor_email": "ops@triggair.com",
      "created_at": "2026-07-01T00:00:00Z"
    }
  ]
}
POST /v1/admin/games/:id/suspend operator session

Suspend a game (takes it dark at the edge)

  • id (path) — Game id.
request
{
  "reason": "ToS abuse"
}
response
{
  "id": "g_1",
  "status": "suspended"
}
POST /v1/admin/games/:id/unsuspend operator session

Unsuspend a game

  • id (path) — Game id.
response
{
  "id": "g_1",
  "status": "active"
}
POST /v1/admin/developers/:id/suspend operator session

Suspend a developer (cascades to all their games)

  • id (path) — Developer id.
request
{
  "reason": "account abuse"
}
response
{
  "id": "d_1",
  "status": "suspended"
}
POST /v1/admin/developers/:id/unsuspend operator session

Unsuspend a developer

  • id (path) — Developer id.
response
{
  "id": "d_1",
  "status": "active"
}
POST /v1/admin/games/:id/keys/:keyId/revoke operator session

Revoke a leaked key (incident lever)

  • id (path) — Game id.
  • keyId (path) — Key id.
response · 204 No Content.
null
GET /v1/admin/games/:id/players/:pid/moderation operator session

Cross-tenant player moderation view

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "player_id": "p_9",
  "reports_against": [],
  "actions": []
}
POST /v1/admin/games/:id/players/:pid/ban operator session

Ban a player (cross-tenant)

  • id (path) — Game id.
  • pid (path) — Player id.
request
{
  "kind": "permanent",
  "reason": "cheating"
}
response
{
  "ok": true,
  "banned": true
}
POST /v1/admin/games/:id/players/:pid/unban operator session

Unban a player

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "ok": true,
  "lifted": true
}
POST /v1/admin/games/:id/players/:pid/mute operator session

Mute a player

  • id (path) — Game id.
  • pid (path) — Player id.
request
{
  "effect": "mute"
}
response
{
  "ok": true
}
POST /v1/admin/games/:id/players/:pid/unmute operator session

Unmute a player

  • id (path) — Game id.
  • pid (path) — Player id.
response
{
  "ok": true,
  "lifted": true
}
GET /v1/admin/games/:id/moderation/appeals operator session

List ban appeals (cross-tenant)

  • id (path) — Game id.
response
{
  "appeals": [
    {
      "id": "ap_1",
      "state": "pending"
    }
  ]
}
POST /v1/admin/games/:id/moderation/appeals/:ap/decide operator session

Decide a ban appeal

  • id (path) — Game id.
  • ap (path) — Appeal id.
request
{
  "grant": true
}
response
{
  "appeal": {
    "id": "ap_1",
    "state": "granted"
  }
}
POST /v1/admin/developers/:id/impersonate operator session

Mint a developer impersonation token

Returns a session-lived token to act AS the developer (full read/write) on /v1/dev/*, for support. Audited.

  • id (path) — Developer id.
response
{
  "token": "eyJ…",
  "developer": {
    "id": "d_1",
    "email": "dev@x.com"
  },
  "expires_at": "2026-07-11T00:00:00Z"
}

Docs & meta

GET /openapi.json no auth

This OpenAPI 3.1 definition

response
{
  "openapi": "3.1.0"
}
GET /llms.txt no auth

Agent index (concise integration guide)

GET /llms-full.txt no auth

Full agent integration guide

GET /dropin no auth

Copy-paste Drop-In prompt

GET /docs/errors/:code no auth

Per-error-code fix doc

  • code (path) — Error code.
GET /status no auth

Liveness + resolved game/env echo

response
{
  "status": "ok",
  "game_id": null,
  "env": "prod"
}
GET /healthz no auth

Health check

GET /time no auth

Authoritative server time (for client clocks)

response
{
  "now": "2026-07-10T00:00:00Z",
  "epoch_ms": 1783000000000
}

MCP

POST /v1/mcp developer session

Model Context Protocol endpoint (streamable HTTP, JSON-RPC 2.0)

The agent-facing management surface: the triggair_* tools (configure games/boards/economy/flags/moderation/compliance, verify_integration, search_docs) over the Web-standard streamable-HTTP MCP transport, stateless JSON mode. Point an MCP client (Cursor, Claude, …) here with your developer session token as `Authorization: Bearer <jwt>`. Tool calls run in-process against your own games — ownership is enforced exactly as on /v1/dev/*.

request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}
response · JSON-RPC result
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "triggair_list_games"
      }
    ]
  }
}