CI and agents
Publish from CI or an AI agent with an organization API key. Permissions, environment variables, a GitHub Actions workflow and safe retries.
Everything a studio does on Depot goes through the public API, and the depot CLI is a client of that API. A CI job or an AI agent can create a game, push builds, move channels and publish a store page with the same commands you run by hand. It authenticates with an organization API key instead of a person's sign-in.
Organization API keys
An API key (dpk_…) belongs to your organization, not to a person. It keeps working if the person who made it leaves, and it can only do what its permissions allow.
Create one while signed in as yourself:
depot api-key create --name ci-push \
--permissions release:publish,channel:write,game:read \
--expires-in-days 90 --out ci-push.keyProp
Type
The secret is shown once. Depot stores only a hash of it and can't show it again. Put it in your CI's secret store and delete the local file.
Only people manage keys. Creating a key needs a signed-in member with the apiKey:create permission (owners and admins), and an API key can never create, rotate or revoke keys or change members.
Permissions
Give each key only what its job needs. A key's permissions must be a subset of your own role's permissions when you create it.
| Permission | Lets the key |
|---|---|
game:create | Create games. |
game:read | Read games, releases, channels, packages and store pages. |
game:update | Edit games and store pages, upload art and media, publish pages, define packages. |
release:publish | Push releases. |
channel:write | Move channels, including rollbacks. |
signingKey:manage | Enroll and revoke signing keys, and register a package's content key on its first push. |
keys:issue, keys:read, keys:revoke | Issue, list and revoke game key batches. |
entitlement:grant, entitlement:read, entitlement:revoke | Grant, list and revoke players' entitlements. |
retailer:delegate | Let a retailer organization issue keys for your packages. |
retail:issue | Issue keys under a delegation your organization holds. |
A key can't hold organization, member, invitation or apiKey permissions.
Common sets:
| Job | Permissions |
|---|---|
| Push builds and move channels | release:publish,channel:write,game:read |
| Push builds for testers, promote by hand | release:publish,game:read |
| Publish the store page too | add game:update |
| An agent that sets up a new game end to end | game:create,game:read,game:update,release:publish,channel:write,signingKey:manage |
Rotate and revoke
depot api-key list
depot api-key rotate --id <key id> --grace-seconds 3600 --out ci-push.key
depot api-key revoke --id <key id>rotate creates a replacement with the same name and permissions. The old key stops at once, or after --grace-seconds so you have time to update your secret store. revoke stops a key immediately. Every create, rotate and revoke is in the audit log.
Environment variables
| Variable | What it does |
|---|---|
DEPOT_API | The API base URL. Use https://api.staging.depot.games. |
DEPOT_TOKEN | The organization API key. The CLI uses it before any stored key or depot login session. |
DEPOT_CONFIG_DIR | Where the CLI looks for config, signing keys and content key backups. Default ~/.config/depot. |
DEPOT_STATE_DIR | Where the CLI keeps staged builds and caches. Default ~/.local/state/depot. |
Check what a job is running as:
depot auth statusWith an API key, this prints the key's organization and permissions.
What a push job needs
A push from CI needs three secrets, not one:
- The API key, as
DEPOT_TOKEN. - The signing key file, after you've enrolled it with
depot keys enroll. The file records which key id it has for each game, so copy it after enrolling. Pass it with--key. - The content key backup for each package you push, from
~/.config/depot/content-keys/. Pass it with--content-key.
Do the first push of each package from your own machine. The first push generates the package's content key and registers it, which needs signingKey:manage. After that, a key with release:publish and channel:write is enough.
The signing key and content key files are as sensitive as the API key. Anyone with all three can publish builds of your game. Keep them in your CI's secret store, never in the repository.
GitHub Actions example
This workflow builds a Linux build on every tag and pushes it to stable. Store DEPOT_TOKEN, DEPOT_SIGNING_KEY (the signing key file's contents) and DEPOT_CONTENT_KEY (the content key backup's contents) as repository secrets. Replace the build step with your own.
name: Release
on:
push:
tags: ["v*"]
jobs:
push:
runs-on: ubuntu-latest
env:
DEPOT_API: https://api.staging.depot.games
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Build the game
run: ./scripts/build-linux.sh out/linux
- name: Install the depot CLI
# There's no prebuilt download yet: build it from the Depot source
# (see "Install the CLI") or restore a binary from your cache.
run: depot --version
- name: Write the signing and content keys
env:
DEPOT_SIGNING_KEY: ${{ secrets.DEPOT_SIGNING_KEY }}
DEPOT_CONTENT_KEY: ${{ secrets.DEPOT_CONTENT_KEY }}
run: |
umask 077
printf '%s' "$DEPOT_SIGNING_KEY" > "$RUNNER_TEMP/signing-key.json"
printf '%s' "$DEPOT_CONTENT_KEY" > "$RUNNER_TEMP/content-key.json"
- name: Push to Depot
run: |
depot push --game my-game --platform linux-x64 \
--release "${GITHUB_REF_NAME#v}" \
--input out/linux --launch-file launch-linux.json \
--key "$RUNNER_TEMP/signing-key.json" \
--content-key "$RUNNER_TEMP/content-key.json" \
--channel stable --note "Release ${GITHUB_REF_NAME}" \
--jsonNotes:
- Re-running a failed job is safe. An unfinished push of the same name resumes. A published release name is refused with a
409, so a re-run never publishes twice. If the release was published but the channel didn't move, move it withdepot channel set. - To ship several platforms, run one push per platform, each with its own
--platform, input folder and launch file. Each platform'sstablechannel moves on its own. - To promote by hand instead, drop
--channeland--noteand move the channel later withdepot channel set.
Driving Depot from an AI agent
An agent can publish a game end to end with the CLI, the same way a person does. Give it an API key with the permissions from the table above and these two documents:
- The docs as text:
https://docs.staging.depot.games/llms.txtis an index of these docs for language models.llms-full.txthas every page in one file. - The API description:
https://api.staging.depot.games/v1/openapi.jsonis the OpenAPI 3.1 document for every route, with the permission each one needs.
Tips for agents:
- Pass
--jsonwhere a command has it, and read the JSON. Most commands without--jsonprint JSON anyway. - Run
depot auth statusfirst to see which organization and permissions the key has. - Never put a key on the command line.
DEPOT_TOKENanddepot auth login(which reads from standard input) keep it out of shell history and logs. - Errors from the API are problem+json objects with a stable
titlecode, such asrevision_mismatchorforbidden. Branch on the code, not on the message. See the API overview.
A full setup an agent can run looks like this. Everything except the first sign-in and key creation works with an API key:
depot game create --slug my-game --name "My Game"
depot keys generate && depot keys enroll --game my-game
depot push --game my-game --platform linux-x64 --release 1.0.0 \
--input build/ --launch-file launch.json --channel stable --note "First build."
depot game art --game my-game --kind cover --file art/cover.png
depot game page --game my-game --tagline "A short line about the game." \
--short-description "Shown in link previews." --access free --publish publicThe last command prints the page URL.
Idempotency keys
Networks fail. When a request times out, you don't know if it happened. Idempotency keys make a retry safe.
Send an Idempotency-Key header on a POST. If Depot has already handled a request with the same key from the same credential on the same route, it returns the stored response instead of acting again, with the header idempotent-replay: true. If you reuse the key with a different body, you get 422 idempotency_mismatch.
- Keys are 8 to 128 characters of letters, digits,
.,_,:and-. - Use a new key for each distinct action, and the same key for retries of that action.
- Some requests require a key: granting an entitlement and issuing a batch of game keys. For game keys, a retry returns the batch without the codes, because codes are delivered once.
The CLI uses idempotency keys for you where it matters. depot game-keys create takes --idempotency-key so you can retry a timed-out batch without issuing a second one:
depot game-keys create --game my-game --quantity 100 --out keys.txt \
--idempotency-key launch-batch-2026-12Channel moves don't need an idempotency key: they're compare-and-swap on the channel's revision. See Channels and releases.