Skip to Content
Depot docs

API overview

Base URL, authentication, organization routes, errors, idempotency, concurrency, pagination, rate limits and caching for the Depot REST API.

Everything a studio does on Depot goes through one public REST API: creating games, pushing releases, moving channels, publishing store pages, issuing keys and granting access. The depot CLI is a client of this API, so anything the CLI does, your own tools and agents can do too.

The pages in this section document every operation, generated from the API's OpenAPI document.

Base URL

https://api.staging.depot.games

Depot runs on staging today. All routes are under /v1.

The OpenAPI 3.1 document is at https://api.staging.depot.games/v1/openapi.json. Each operation lists the permission it needs.

Authentication

Send a bearer token in the Authorization header:

curl https://api.staging.depot.games/v1/whoami \
  -H "Authorization: Bearer dpk_your_key"

There are two kinds of credential for studio work:

CredentialUse it forLooks like
Organization API keyCI, integrations and agents. Acts for one organization, with the permissions you gave it.dpk_…
Signed-in sessionA person, through the CLI or launcher. Acts with the person's current role in each organization.an access token from Depot's sign-in

Create API keys with depot api-key create. See CI and agents for permissions.

Tokens go only in the Authorization header. The API never accepts them in URLs.

GET /v1/whoami tells you what a credential is. For an API key, it returns the key id, its organization and its permissions:

{
  "kind": "api_key",
  "key_id": "…",
  "organization": { "id": "…", "name": "My Studio", "slug": "my-studio", "created_at": "…" },
  "permissions": { "game": ["read"], "release": ["publish"], "channel": ["write"] }
}

Some routes need no credential at all, such as published store pages under /v1/public/… and the launcher catalog.

Organization routes

Studio operations always name the organization in the path:

/v1/organizations/{org}/games
/v1/organizations/{org}/games/{id}/releases
/v1/organizations/{org}/games/{id}/channels/{platform}/{name}

{org} is the organization's id. Get it from GET /v1/whoami (API keys) or GET /v1/organizations (people). Game ids look like game_ followed by 32 hex characters.

On every request, Depot checks that the credential belongs to that organization right now and holds the permission the route needs. A removed member loses access on their next request. A game, release or key that belongs to another organization answers 404, so ids can't be probed across studios.

Only people manage people and keys. Routes for members, invitations and API keys refuse API keys with 403.

Errors

Errors use RFC 9457 problem details, with the content type application/problem+json:

{
  "type": "https://depot.games/problems/forbidden",
  "title": "forbidden",
  "status": 403,
  "detail": "this API key lacks channel:write",
  "request_id": "req_0123456789abcdef0123456789abcdef"
}
  • title is a stable code. Branch on it, not on detail, which is for people and may change.
  • request_id matches the x-request-id response header. Include it when you report a problem.
  • Some errors carry extra fields. A channel move that lost a race says where the channel is now:
{
  "type": "https://depot.games/problems/revision_mismatch",
  "title": "revision_mismatch",
  "status": 409,
  "detail": "channel changed since you read it",
  "request_id": "req_0123456789abcdef0123456789abcdef",
  "current_revision": 4,
  "current_release_id": "rel_0123456789abcdef0123456789abcdef"
}

Common codes:

StatusCodesMeaning
400bad_requestA field is missing or invalid. detail names it.
401unauthorized, unauthenticated, token_expiredNo credential, or it's invalid, expired or revoked.
403forbiddenThe credential is valid but lacks the permission.
404not_foundNo such resource in this organization, or no such route.
409conflict, revision_mismatch, game_archivedThe request conflicts with the current state. Read again and decide.
413payload_too_large, too_largeThe body or upload is too big.
422idempotency_mismatch, bad_signature, hash_mismatch, untrusted_key, invalid_imageThe request is well formed but can't be accepted.
429rate_limitedToo many requests of this kind. See Rate limits.
5xxinternal, storage_error, …A problem on Depot's side. Retry with backoff.

Idempotency keys

Send an Idempotency-Key header on a POST to make retries safe:

curl -X POST "https://api.staging.depot.games/v1/organizations/$ORG/games" \
  -H "Authorization: Bearer $DEPOT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: create-my-game-1" \
  -d '{"slug": "my-game", "name": "My Game"}'
  • The same credential, route, key and body returns the stored response instead of acting again. The replay carries the header idempotent-replay: true.
  • The same key with a different body is 422 idempotency_mismatch.
  • Keys are 8 to 128 characters: letters, digits, ., _, : and -. They're scoped to the credential and organization.
  • Only successful responses are stored. If a request failed, a retry with the same key runs it again.
  • Granting an entitlement and issuing a batch of game keys require a key. A retried game key batch returns the batch without its codes, because codes are delivered once.

Optimistic concurrency

Resources that people and tools edit at the same time have a revision that goes up on every change. Send it back as expected_revision and Depot refuses the write with 409 if someone changed the resource since you read it:

  • Channels (PUT …/channels/{platform}/{name}): expected_revision is required. Use null to create a channel. A stale value gets 409 revision_mismatch with the current revision and release. A retried move can never move the channel twice.
  • Games, store pages and storefront profiles (PATCH, and publishing a page): expected_revision is optional. Leave it out to overwrite. A stale value gets 409 conflict with the current revision.

Pagination

List routes take limit (1 to 200) and cursor, and return a next_cursor:

{ "releases": [ … ], "next_cursor": "…" }

Pass next_cursor as cursor to get the next page. null means there are no more pages.

Rate limits

Some operations are limited per organization, player or credential over a fixed time window. Examples are creating organizations and API keys, sending invitations, issuing game key batches, granting entitlements, redeeming keys and claiming games. Over the limit, you get 429 rate_limited. Wait and retry later, with backoff. Don't retry in a tight loop.

Caching

  • Responses are cache-control: no-store unless a route says otherwise. Anything tied to a credential is never cached.
  • Published store pages and studio pages under /v1/public/… send an ETag and are cached for about a minute. Send If-None-Match to get 304 Not Modified when nothing changed. After you publish a page, it can take that long to show everywhere.
  • The OpenAPI document is cached for a few minutes.

Uploads never pass through the API

Game builds don't travel through the API, and neither do store page videos. When you push, the API hands out short-lived presigned URLs, and the client uploads each object straight to storage. Large objects use multipart uploads, and the API completes them from storage's own list of parts. Players download the same way, with presigned URLs that expire after 15 minutes.

The API checks every uploaded object's size and hash, and the release's signature, before it publishes anything. See Push builds.

The depot push command does all of this for you. Use the upload routes directly only if you're writing your own publishing client.

Try it in the browser

Each operation page in this section has a playground that sends real requests to staging from your browser. Paste an organization API key to authenticate.

Use a staging key with only the permissions you need for the test, and revoke it when you're done. Never paste production secrets into the playground. Requests are real: a playground POST creates real resources in your organization.

On this page