API Docs

REST API overview

AdminUpdated Sep 22, 2026

REST API overview

The Cool GPT Games REST API is the source of truth for everything you do as a developer: creating games, uploading builds, reading your stats, running tournaments, configuring live-ops and webhooks. The Publisher SDK, the CLI and the MCP server are thin wrappers over the endpoints on this page.

This page covers the conventions that apply to every endpoint, then gives an index of every developer-facing endpoint with links to the page that documents it in detail.

Base URL

https://api.coolgptgames.com

Every path in these docs is relative to that origin, for example GET https://api.coolgptgames.com/v1/me/games.

All endpoints live under the /v1/ prefix. Two unversioned health endpoints also exist (GET /health, GET /ready); they are for uptime monitoring and carry no data you need.

Authentication

There are two ways to authenticate. Both identify your account, and both are checked on every request.

Method

How it's sent

Who uses it

Scopes

API key

Authorization: Bearer <key> header

Your servers, scripts, CI, the CLI, the Publisher SDK, the MCP server

Limited to the scopes chosen when the key was created

Interactive session

Set by signing in on coolgptgames.com (browser cookie, or a session bearer token)

The website and dashboard

Full access to your account; never scope-limited

API keys

Create keys in the dashboard under Dashboard → API keys (see API keys & scopes). Send the key as a bearer token:

curl https://api.coolgptgames.com/v1/me/games \
  -H "Authorization: Bearer $COOLGPTGAMES_API_KEY"
  • New keys start with ak_. Keys created before September 2026 start with arc_live_ and keep working unchanged. Treat both the same way.

  • API keys are accepted only in the Authorization: Bearer header. A key placed in a cookie is ignored.

  • A revoked key stops working immediately, on the very next request.

  • A key belonging to a banned or deleted account stops working immediately.

  • Key requests are exempt from CSRF checks (they can't ride an ambient browser cookie), so no extra headers are needed.

What API keys can never do

Some routes manage your account itself (its email, its keys, its payout details, its public profile) or spend and move coins. Those routes require an interactive session and reject any API key, whatever its scopes, with:

{
  "error": {
    "code": "session_required",
    "message": "API keys can't manage your account — sign in to the dashboard."
  }
}

(HTTP 403.) This means a leaked key, even a full-access one, can't mint new keys, revoke your other keys, change your email, delete your account, redirect your payouts, or spend your coins. The session-only routes are:

Method & path

What it does

POST /v1/me/keys

Create an API key

GET /v1/me/keys

List your API keys

DELETE /v1/me/keys/:id

Revoke an API key

POST /v1/me/connect/onboard

Start payout (bank) onboarding

PATCH /v1/me/profile

Edit your public profile

POST /v1/me/avatar

Upload your avatar

POST /v1/me/banner

Upload your profile banner

POST /v1/me/email

Change your account email

DELETE /v1/me/account

Delete your account

PATCH /v1/me/settings

Notification preferences

POST /v1/me/interests

Onboarding interests

POST /v1/me/age-eligibility

Ad-personalisation age declaration

POST /v1/daily/claim

Claim the daily coin reward

POST /v1/gifts/:handle

Gift coins to a friend

POST /v1/shop/buy

Buy from the platform cosmetics shop

POST /v1/season/claim/:tier

Claim a season reward tier

POST /v1/games/:id/purchase

Buy an in-game store item

POST /v1/dev/tournaments with prizePoolCoins above 0

Fund a tournament prize pool (free tournaments still accept a key)

Do these signed in on coolgptgames.com.

Calling from a browser

The API only accepts cross-origin browser requests from Cool GPT Games' own sites. Call the API from your server, script or CI job, not from JavaScript running on your own website. It's also the right choice for security: an API key in browser code is a leaked key.

Inside a game you never call the REST API directly. Games talk to the platform through the GameSDK (see GameSDK core reference), and the player page makes the API calls for you.

Session cookies and CSRF

This only matters if you are building against the API with a browser session rather than a key. Cookie-authenticated POST, PUT, PATCH and DELETE requests must echo the value of the csrf cookie in an X-CSRF-Token header, or they fail with 403 forbidden ("CSRF token missing or invalid"). Bearer-token requests (API keys and session tokens) are exempt.

Roles

Every account can both play and publish games. Creator endpoints check that the caller owns the game in question. Acting on someone else's game returns 403 forbidden (or 404 not_found where the game's existence shouldn't be revealed).

Requests

  • JSON bodies. Send Content-Type: application/json when there is a body. Don't send that header on requests without a body (for example DELETE /v1/games/:id), because an empty body declared as JSON is rejected.

  • Binary bodies. A few endpoints take raw bytes instead of JSON:

    • Bundle uploads to the presigned uploadUrl: Content-Type: application/zip.

    • Game images (POST /v1/games/:id/media): image/png, image/jpeg, image/gif or image/webp. The real type is detected from the file's bytes, not the header.

  • Body size. Every endpoint accepts at most 1 MB of body unless documented otherwise. Image uploads allow 5 MB. Bundles go straight to storage through the presigned URL, with a limit that depends on the runtime. See Errors & limits. - IDs vs slugs. Write and owner endpoints take the game's id (:id). Public read endpoints take its slug (:slug). POST /v1/games returns both. GET /v1/me/games lists both for all your games.

  • Timestamps are ISO 8601 strings in UTC. Date-only fields (such as daily earnings) are YYYY-MM-DD.

  • Unknown fields in a JSON body are ignored.

Responses

Successful responses are JSON objects, usually 200 OK. Creation endpoints return 201 Created (POST /v1/games, POST /v1/games/:id/versions, POST /v1/me/keys, POST /v1/dev/webhooks). Most action endpoints that have nothing else to return send { "ok": true }.

Money amounts in creator earnings responses are US dollars, in fields ending Usd. Coins (fields ending Coins, or named coins) are the platform's virtual currency. They are not real money and can't be cashed out.

Errors

Every error handled by the API uses the same envelope:

{
  "error": {
    "code": "cover_required",
    "message": "Add a cover image before publishing your game.",
    "details": {}
  }
}

Field

Meaning

code

Stable, machine-readable string. Branch on this.

message

Human-readable explanation. Can change wording at any time, so don't parse it.

details

Optional. Present on some errors, notably validation_error, where it holds per-field problems.

The HTTP status tells you the class of the error (400 bad input, 401 not authenticated, 403 not allowed, 404 not found, 409 conflict, 429 rate limited, 5xx server-side). One exception: requests to a path that doesn't exist at all return the web framework's default 404 body ({ "message", "error", "statusCode" }) instead of the envelope.

The complete list of codes, and every rate and size limit, is in Errors & limits.

Rate limits

All /v1/ requests share a limit of 100 requests per minute per IP address. When you exceed it you get 429 rate_limited with a Retry-After header (in seconds). Many endpoints also have their own tighter limits (uploads, key creation, tournament creation and so on). Those return 429 rate_limited without a Retry-After header. See Errors & limits for the full table.

Build in backoff: on 429, wait Retry-After seconds if present, or about a minute otherwise, then retry.

Pagination

Most list endpoints return a bounded list in one response (for example your games, reports and payouts return at most 200 items, newest first). No paging is needed.

The public catalogue (GET /v1/games) and game reviews (GET /v1/games/:slug/reviews) use cursor pagination:

curl "https://api.coolgptgames.com/v1/games?sort=new&limit=24"
# → { "games": [...], "nextCursor": "MjQ" }
curl "https://api.coolgptgames.com/v1/games?sort=new&limit=24&cursor=MjQ"
# → { "games": [...], "nextCursor": null }   ← last page
  • Pass nextCursor back as cursor to get the next page. null means you've reached the end.

  • Treat cursors as opaque strings. Don't build them yourself.

  • GET /v1/games accepts limit from 1 to 48 (default 24). Reviews come 20 per page.

  • Paging reaches at most about 10,000 items deep. Narrow the query with filters instead of paging past that.

Idempotency

POST /v1/games (create a game) accepts an optional Idempotency-Key header (up to 128 characters). If you retry a create with the same key, you get the same draft back (200 with "idempotent": true) instead of a duplicate:

curl -X POST https://api.coolgptgames.com/v1/games \
  -H "Authorization: Bearer $COOLGPTGAMES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ci-build-4812" \
  -d '{"title":"Space Miner","categorySlug":"arcade"}'

No other endpoint takes an idempotency key. Many writes are naturally safe to repeat:

  • POST /v1/upload/complete for a version that already completed returns 200 with "idempotent": true and the version's current state instead of processing it again.

  • Remote config PUT replaces the whole config.

  • Store items are upserted by sku.

  • Revoking an already-revoked key or cancelling an already-cancelled tournament does nothing new.

Reserving a version is not idempotent: every POST /v1/upload/init or POST /v1/games/:id/versions call reserves a new version number and counts against your hourly upload allowance.

Versioning

The API is versioned in the path (/v1/). There is no version header. Additive changes, such as new endpoints, new optional request fields and new response fields, can ship within v1 at any time, so ignore fields you don't recognise.

Endpoint index

"Auth" says what the caller must be:

  • Public: no credentials needed. - Key or session: an API key or a signed-in session. - Session only: interactive session; API keys get 403 session_required.

  • Player page: called for you by the game player while someone plays. You don't call it directly.

"Scope" is the API-key scope the endpoint checks. A signed-in session is never scope-limited. A publish key also satisfies read. None checked means any valid key for your account works, whatever scopes it holds. See API keys & scopes.

Your games: create, upload, manage

Detailed in Games & uploads API and Packaging & publishing.

Method

Path

Auth

Scope

POST

/v1/games

Key or session

publish

POST

/v1/games/:id

Key or session

publish

DELETE

/v1/games/:id

Key or session

publish

POST

/v1/upload/init

Key or session

publish

PUT

<uploadUrl> returned by upload/init

Presigned URL (no API key)

n/a

POST

/v1/upload/complete

Key or session

publish

POST

/v1/games/:id/versions

Key or session

publish

POST

/v1/games/:id/media?kind=cover|loading|screenshot

Key or session

publish

DELETE

/v1/games/:id/media

Key or session

publish

POST

/v1/games/:id/media/reorder

Key or session

publish

GET

/v1/runtimes

Public

n/a

Your dashboard data (/v1/me/*)

Documented in Creator dashboard endpoints below.

Method

Path

Auth

Scope

GET

/v1/me/games

Key or session

read

GET

/v1/me/games/:id

Key or session

read

GET

/v1/me/games/:id/preview

Key or session

publish

POST

/v1/me/games/:id/versions/:versionId/rollback

Key or session

publish

GET

/v1/me/summary

Key or session

read

GET

/v1/me/earnings

Key or session

read

GET

/v1/me/payouts

Key or session

read

GET

/v1/me/reports

Key or session

read

GET

/v1/me/creator

Key or session

read

GET

/v1/auth/me

Key or session

None checked

POST

/v1/me/connect/onboard

Session only

n/a

API keys

Detailed in API keys & scopes.

Method

Path

Auth

Scope

POST

/v1/me/keys

Session only

n/a

GET

/v1/me/keys

Session only

n/a

DELETE

/v1/me/keys/:id

Session only

n/a

Analytics and remote config

Detailed in Remote config & analytics.

Method

Path

Auth

Scope

GET

/v1/dev/games/:id/analytics

Key or session

analytics

GET

/v1/dev/games/:id/config

Key or session

config

PUT

/v1/dev/games/:id/config

Key or session

config

GET

/v1/games/:slug/config

Public

n/a

Webhooks

Detailed in Webhooks.

Method

Path

Auth

Scope

POST

/v1/dev/webhooks

Key or session

webhooks

GET

/v1/dev/webhooks

Key or session

webhooks

DELETE

/v1/dev/webhooks/:id

Key or session

webhooks

POST

/v1/dev/webhooks/:id/test

Key or session

webhooks

GET

/v1/dev/webhooks/:id/deliveries

Key or session

webhooks

Tournaments

Detailed in Tournaments.

Method

Path

Auth

Scope

POST

/v1/dev/tournaments

Key or session (session only when prizePoolCoins > 0)

tournaments

GET

/v1/dev/tournaments

Key or session

tournaments

POST

/v1/dev/tournaments/:id/cancel

Key or session

tournaments

GET

/v1/tournaments/:id

Public

n/a

GET

/v1/tournaments

Public

n/a

GET

/v1/games/:slug/tournaments

Public

n/a

In-game economy

Detailed in In-game economy.

Method

Path

Auth

Scope

POST

/v1/dev/games/:id/items

Key or session

economy

GET

/v1/dev/games/:id/items

Key or session

economy

DELETE

/v1/dev/games/:id/items/:sku

Key or session

economy

GET

/v1/games/:slug/items

Public

n/a

Public game data

Useful for building your own pages, bots or leaderboards. No credentials needed.

Method

Path

Detailed in

GET

/v1/games

This page (Pagination); Games & uploads API

GET

/v1/games/:slug

Games & uploads API (your own unpublished game needs a session, or a key with read or publish)

GET

/v1/games/:slug/leaderboard

Scores, leaderboards & anti-cheat

GET

/v1/games/:slug/achievements

XP, achievements & social

GET

/v1/games/:slug/reviews

This page (Pagination)

GET

/v1/creators/:handle

Your public creator profile and published games

GET

/v1/creators/:handle/studio

Your public creator progression

Called by the player page (not for direct use)

While someone plays your game, the player page calls these on their behalf, in response to GameSDK calls your game makes. They're listed so you recognise them in traces and limit tables. Don't call them from your own code; use the GameSDK instead. See How games run and the GameSDK pages.

Method

Path

Triggered by

POST

/v1/sessions/start, /v1/sessions/:id/heartbeat, /v1/sessions/:id/end

The player loading and running your game

POST

/v1/sessions/:id/event

GameSDK.trackEvent / GameSDK.reportError

POST

/v1/sessions/:id/xp, /v1/sessions/:id/achievements

GameSDK.awardXp / GameSDK.unlockAchievement

POST

/v1/sessions/:id/score, /v1/sessions/:id/replay

GameSDK.submitScore / replay submission

PUT GET DELETE

/v1/saves

GameSDK.saveData / loadData / deleteSave

POST

/v1/games/:id/purchase

GameSDK.economy.purchase (session only: API keys get session_required)

various

/v1/ranked/*

GameSDK.ranked.*

Creator dashboard endpoints

These endpoints back the creator dashboard. Each returns only your own data. The read endpoints need a key with read (or publish); preview and rollback need publish. A signed-in session can call all of them.

GET /v1/me/games

Lists your games, including drafts, newest-updated first (up to 200). Deleted games are excluded.

{
  "games": [
    {
      "id": "7c1f…",
      "slug": "space-miner-3fa2c1",
      "title": "Space Miner",
      "status": "published",
      "playsTotal": 1824,
      "ratingAvg": 4.3,
      "publishedAt": "2026-09-02T18:11:04.000Z"
    }
  ]
}

status is one of draft, scanning, pending_review, published, rejected, delisted, dmca_removed.

The public GET /v1/games/:slug returns 404 for a game that isn't published. Use this endpoint and GET /v1/me/games/:id to see your drafts.

GET /v1/me/games/:id

The owner-only manage view for one game: metadata, every version with its moderation result, and per-day revenue.

{
  "game": {
    "id": "7c1f…", "slug": "space-miner-3fa2c1", "title": "Space Miner",
    "description": "…", "instructions": "…",
    "status": "published", "visibility": "full",
    "category": { "slug": "arcade", "name": "Arcade" },
    "aiGenerated": true, "aiDisclosure": "…",
    "playsTotal": 1824, "ratingAvg": 4.3, "ratingCount": 51,
    "currentVersionId": "b9e0…",
    "publishedAt": "…", "createdAt": "…", "updatedAt": "…"
  },
  "versions": [
    {
      "id": "b9e0…", "versionNumber": 3, "status": "live",
      "realtimeEnabled": false, "sizeBytes": 481223,
      "runtime": "html5", "runtimeVersion": "1.0.0",
      "createdAt": "…", "isCurrent": true,
      "moderation": { "verdict": "auto_approve", "confidence": 0.97, "status": "complete", "error": null },
      "review": null
    }
  ],
  "analytics": {
    "earningsTotalUsd": 12.4,
    "byDay": [{ "date": "2026-09-20", "validPlays": 88, "impressions": 140, "grossUsd": 0.61, "creatorShareUsd": 0.3 }],
    "reportsOpen": 0
  }
}
  • visibility is limited (reachable by direct link, not yet shown in public discovery) or full.

  • Version status is uploaded, scanning, passed, failed or live.

  • moderation is the automated scan result. verdict is auto_approve, auto_reject or needs_review, and status is queued, running, complete or error.

  • review is the recorded decision, { decision, reasonCodes, notes, at } with decision either approve or reject, when one exists. Human and automated decisions both land here. An automated one has notes of "Approved by automated moderation." or "Rejected by automated moderation.". If a version was rejected, the reason codes are here.

Errors: 404 not_found if the game doesn't exist or was deleted; 403 forbidden if it isn't yours.

When you ship an update to a game that's already live, the game's status stays published while the new version is moderated. Watch the new version's entry in versions (by its id) to see the verdict: live means approved, failed rejected, passed waiting for a human.

GET /v1/me/games/:id/preview

Returns a private, short-lived play link for your game's build, including a build that isn't published yet. Use it to play-test before or during review.

Query

Meaning

versionId (optional)

Preview a specific version. Default: the current version, or else the newest upload.

{ "playUrl": "https://…?t=…", "version": { "id": "…", "versionNumber": 4, "status": "scanning" } }

The link carries an access ticket that expires after 15 minutes. Request a new one each time you need it.

Errors: 400 no_build (nothing uploaded yet), 400 runtime (runtime unavailable for preview), 403/404 as above.

POST /v1/me/games/:id/versions/:versionId/rollback

Makes an earlier version the live one again, instantly and without re-review. Only versions that were approved and published before (status live) qualify. A passed version, which is still waiting for a human reviewer, doesn't. The game becomes published with full visibility.

{ "ok": true, "currentVersionId": "a13c…", "versionNumber": 2 }

Errors: 400 not_approved (that version was never approved and published), 409 game_removed (the game was delisted or DMCA-removed), 404 (unknown game or version, or a deleted game).

GET /v1/me/summary

Headline numbers for your account.

{
  "playsTotal": 5120, "gamesTotal": 4, "gamesLive": 3, "gamesPending": 1, "reportsOpen": 0,
  "lifetimeEarningsUsd": 41.2, "last30EarningsUsd": 9.85,
  "availableUsd": 41.2, "pendingPayoutUsd": 0,
  "payoutStatus": "none", "payoutMinimumUsd": 50, "creatorRevenueSharePct": 50
}

GET /v1/me/earnings

Per-day earnings across all your games.

Query

Meaning

from (optional)

First date to include, YYYY-MM-DD

to (optional)

Last date to include, YYYY-MM-DD

{
  "totals": { "grossUsd": 82.4, "creatorShareUsd": 41.2, "validPlays": 5020, "impressions": 7310 },
  "byDay": [{ "date": "2026-09-20", "grossUsd": 1.2, "creatorShareUsd": 0.6, "validPlays": 88, "impressions": 140 }],
  "payoutMinimumUsd": 50
}

GET /v1/me/payouts

Your payout history (up to 200, newest first) and your current unpaid balance.

{
  "payouts": [{ "id": "…", "periodStart": "…", "periodEnd": "…", "amountUsd": 55.1, "status": "paid", "createdAt": "…", "paidAt": "…" }],
  "availableUsd": 12.4,
  "payoutStatus": "none",
  "payoutMinimumUsd": 50
}

Setting up where payouts go (POST /v1/me/connect/onboard) is session-only. Do it in the dashboard.

GET /v1/me/reports

Player reports filed against your games (up to 200, newest first). This is read-only: moderators resolve reports, and you can't dismiss them.

{
  "reports": [{ "id": "…", "gameId": "…", "gameTitle": "Space Miner", "gameSlug": "space-miner-3fa2c1", "reasonCode": "broken", "details": "…", "status": "open", "createdAt": "…" }],
  "openCount": 1
}

GET /v1/me/creator

Your creator progression (creator level, XP and unlocked perks), recalculated on each call. Higher creator levels raise your hourly upload allowance (see Errors & limits).

GET /v1/auth/me

Returns the account the credentials belong to, as { "user": { … } }, or 401 unauthorized. Useful as a "does this key work?" check.

See also

Was this page helpful?