# Access: credentials, roles and permissions (/docs/concepts/access)



Everything that changes a Domino project goes through a **credential**: a person's Console session, a key for an app or an agent, or a sign-in the CLI saved. What a credential may do depends on three things:

1. **Who it acts for.** A person's credentials carry that person's roles. A key that no person owns carries only the permissions it was given.
2. **Its own limits.** Most keys work in one project and one environment, and some kinds can never do certain things, whatever their permissions say.
3. **The environment.** Live, where your real members are, accepts fewer changes than test. See [what each credential may do in live](#what-each-credential-may-do-in-live).

Domino checks all three on every request, whichever tool sends it. Hiding a button in Console is never what stops an action.

## Organization admins and project roles [#organization-admins-and-project-roles]

People join your organization through Console, which uses Clerk for accounts and invitations. Each person is either an **organization admin** or a member.

* **Organization admins** hold every permission in every project, in test and live, without being granted anything. Only they manage the team's access, service and automation keys, app login keys, and the project itself: its name, deletion and transfer.
* **Members** can do nothing in a project until an admin grants them a role there, in **Settings → Team**.

A member can hold several roles in a project:

| Role                  | What it's for                                                                                            | Permissions                                                  |
| --------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| Editor                | Publish quests, manage stock and points, and send tracker records and enroll members from your backend   | Read, Publish, Inventory, Points, Ingest, Enroll, Dashboards |
| Reviewer              | Review evidence and retry attempts                                                                       | Read, Review, Dashboards                                     |
| Staff                 | Confirm quests and hand over rewards                                                                     | Read, Confirm, Handover, Dashboards                          |
| Viewer (experimental) | See the project's dashboards and nothing else: no members, evidence or settings. For a sponsor or client | Dashboards                                                   |

### Permissions [#permissions]

| Permission | Allows                                                                                                                                                         |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read       | Looking up the project: quests, members, progress, balances and analytics                                                                                      |
| Publish    | Changing what the program is in test, preparing promotions, approving them in Console, and running day-to-day controls such as pausing a quest                 |
| Review     | Deciding submissions and retrying attempts                                                                                                                     |
| Confirm    | Confirming that a member completed a quest, such as checking them in at an event                                                                               |
| Handover   | Handing over rewards that members collect in person, and correcting those handovers                                                                            |
| Inventory  | Managing offers and stock                                                                                                                                      |
| Points     | Awarding, spending and correcting points                                                                                                                       |
| Ingest     | Sending records to custom [trackers](/docs/concepts/trackers)                                                                                                  |
| Enroll     | Enrolling members in quests and campaigns from your server, which vouches for their consent                                                                    |
| Dashboards | Reading the project's [dashboards](/docs/build/dashboards), which show totals and never an individual member. Read includes it, and no key holds it on its own |
| Access     | Administering access. Only organization admins hold it: no role includes it, and no key can hold it                                                            |

### Grants are per environment [#grants-are-per-environment]

An admin chooses, for each member, the roles and whether they apply in test, live or both. The same roles apply in each environment chosen. A grant can also have an end date: after it, the grant gives nothing, and **Settings → Team** shows it as **Expired** until an admin changes it. A member granted Editor in test only can build and try quests, and can neither read nor change anything in live.

Changes to a grant, and removing someone from the organization, take effect on that person's next request, for every credential that acts for them.

> [!DEV]
> Roles are `editor`, `reviewer` and `staff`; permissions are `read`, `publish`, `review`, `confirm`, `handover`, `inventory`, `access`, `points`, `ingest` and `enroll` (`roleSchema`, `permissionSchema` and `rolePermissions` in `@domino-sdk/relay/server`). The experimental `viewer` role and `dashboards` permission are in `projectRoleSchema`, `projectPermissionSchema` and `projectRolePermissions`; responses list them in `projectRoles` and `projectPermissions`, beside the 1.x `roles` and `permissions`. An admin is a member with Clerk's `org:admin` role. `management.access()` lists the permissions the current credential has in the selected project and environment. A grant is `{ subject, roles, environments, expiresAt? }` (`projectGrantSchema`, `expiresAt` in epoch milliseconds), set with `PUT /management/v1/grants` from an admin's Console session.

## Kinds of credentials [#kinds-of-credentials]

| Credential                       | Who gets it, and how                                                                                                            | What it acts as, and where                                                                                                            |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Console session                  | Anyone who signs in to Console                                                                                                  | That person, in any project and environment their roles reach                                                                         |
| Personal API key, `ak_…`         | Anyone with a role in the project other than Viewer alone, in **Settings → API keys**, or `domino tokens create` for a test key | Its owner, with the permissions chosen for it, in one project and one environment                                                     |
| CLI session, `dc_…`              | `domino login`, approved in Console                                                                                             | That person, in any project their roles reach, with the [CLI's limits](#what-each-credential-may-do-in-live)                          |
| Playground session, `dc_…`       | Starting a [Playground](/docs/build/playground)                                                                                 | That person, in its one project and environment                                                                                       |
| AI assistant connection, `oat_…` | [Connecting an AI assistant](/docs/agents/connect) and signing in                                                               | That person, in any project their roles reach, with the CLI's limits and no changes in live                                           |
| Service key, `dsk_…`             | An organization admin, in **Settings → API keys**                                                                               | An app's own backend, in one project and one environment                                                                              |
| Automation key, `dak_…`          | An organization admin, in **Settings → API keys**                                                                               | A headless agent or CI, in all of the organization's projects or chosen ones, changing test only                                      |
| Hosted app key, `dhk_…`          | Domino, for each [hosted app](/docs/concepts/hosting) server and `domino dev`                                                   | The hosted app's server, in its project: test for previews, staging and development, live for the live app                            |
| App login key, `di_…`            | An organization admin, in **Settings → App login keys**, which can save it straight into a hosted app's secrets                 | Your server, signing members in with [your app's login](/docs/build/app-login) for one project and environment; it calls nothing else |

The first five act for a person and stop working when that person leaves the organization. Service, automation, hosted app and app login keys belong to no person.

A `domino login` request waits 15 minutes for approval. Console's approval page shows when it was made, the country it came from and the CLI version and system that sent it. Approve only a request your own terminal just made: the session it creates acts as you. Connecting a service through its own sign-in, such as [Klaviyo](/docs/build/klaviyo), is finished in Console by the person who started it, so a credential that belongs to no person can't start one.

### Personal API keys [#personal-api-keys]

A personal key acts as you, so it can never do more than you can. You choose its project, its environment and a subset of your permissions in them when you create it, and it can't hold Access. A key from Console expires after 30 days; a test key from `domino tokens create --days` lasts 1 to 90. It stops working when you leave the organization, and loses any permission you lose.

A key holding Ingest can be limited further, to sending records to chosen custom trackers only. Use one for an indexer or a partner's server that should not reach anything else.

You can create a test key from the CLI. A live key is created in Console only.

### Service and automation keys [#service-and-automation-keys]

These belong to no person, so they keep working when the admin who made them leaves. Each has the permissions chosen when it was created, never Access, and never expires unless an expiry was chosen. An admin can rotate a key by adding a new secret, then revoking the old one once nothing uses it, or revoke the whole key at once.

* **A service key** is for an app's own backend, outside Domino hosting: awarding points, sending tracker records, qualifying referrals. It works in one project and environment. See [connect your app](/docs/start/existing-app#5-call-domino-from-your-backend).
* **An automation key** is for a coding agent or CI with nobody there to approve `domino login`. It reaches all of the organization's projects or chosen ones, holds at most Read, Publish, Inventory, Points, Ingest and Enroll, and changes nothing in live. See [run an agent without signing in](/docs/agents/automation).

### Hosted app keys [#hosted-app-keys]

Domino gives every staged and live Worker app, and the dev server `domino dev` starts, its own key, so nobody pastes a credential into a hosted app. It holds exactly Read, Points and Ingest, in its own project and environment. A `domino dev` key ends when the dev server stops, or after 12 hours. See [call the management API from your server](/docs/build/hosted-app#call-the-management-api-from-your-server).

### What keys and the CLI don't do [#what-keys-and-the-cli-dont-do]

* **Administer access.** Grants, the team and the project's name, deletion and transfer take an organization admin's Console session. CLI sessions and AI assistant connections are refused too.
* **Create another credential.** Only an admin's Console session creates service, automation and app login keys. Personal keys are created by their owner in Console, or as test keys from a CLI session; no key creates or revokes one.
* **Act as test members.** Service and hosted app keys are an app's own server, so they can't create [test members](/docs/build/testing) or act as them. Console sessions, personal keys, CLI and Playground sessions, AI assistant connections and automation keys can, in test, when they act with Publish.

> [!DEV]
> Every request sends `Authorization: Bearer <credential>`, and Domino tells kinds apart by the prefixes above. Old `dm_` tokens are refused with 401 `invalid-credential`; replace them with a personal API key. When an API server has refused one address 60 times within 10 minutes for a missing or refused credential or a path the API does not serve, it answers that address's management, MCP and Git requests 429 `rate-limited` with `Retry-After` until the 10 minutes end, valid credentials included. A personal key or MCP token counts only when Clerk does not recognize it. Successful requests and Git's first, credential-less request of a fetch or push do not count. A credential fixed to a project or environment refuses a request whose `X-Relay-Project` or `X-Relay-Environment` names another, with 403 `scope-mismatch`. Refusals use the [error codes](/docs/reference/errors#sign-in-or-use-another-credential): `permission-required` for a missing permission or admin role, `credential-not-allowed` when the action needs another kind of credential, and `console-approval-required` for the live limits below. Personal keys are managed with `domino tokens` and `management.tokens`; service keys with `management.serviceKeys` and automation keys with `management.automationKeys`, from an admin's Console session.

## What each credential may do in live [#what-each-credential-may-do-in-live]

Live accepts two kinds of change, as [test and live](/docs/concepts/environments) explains. **Definitions** (quests, rewards, point ledgers, code and the rest of what your program is) reach live only through a [promotion](/docs/concepts/promotion) a person approves in Console. **Operations** (pausing a quest, reviewing a submission, correcting points) happen in live directly, under the credential's permissions.

No credential changes definitions in live, Console included, and new code reaches live only through a promotion: make the change in test and promote it. For everything else:

| Credential                     | Decide a promotion | Day-to-day operations                   |
| ------------------------------ | ------------------ | --------------------------------------- |
| Console session                | Yes, with Publish  | Yes                                     |
| Personal API key for live      | No                 | Yes, with its permissions               |
| Service key for live           | No                 | Yes, with its permissions               |
| Hosted app key of the live app | No                 | Read, points and tracker records        |
| CLI session                    | No                 | Only the [list below](#the-cli-in-live) |
| AI assistant connection        | No                 | None: reads only                        |
| Automation key                 | No                 | None: reads only                        |

Every credential that reaches live and holds Read can look things up there. A key or session fixed to test can't read live.

### The CLI in live [#the-cli-in-live]

A `domino login` session may make only these changes in live:

* Prepare a promotion with the deprecated `domino deploy --promote` (`domino promote` does the same from test), and roll back a promotion. A person still approves each promotion in Console.
* Preview catalog changes and submit them for review.
* Set app secrets, custom domains and plugin connections, which belong to each environment and never move with a promotion.
* Upload images a preview needs.
* Prepare, check, cancel, stop and revert a [member import](/docs/use/imports). Only Console applies one in live.
* Revoke your personal keys. The CLI creates and changes test keys only.

For anything else in live, use Console, or create a live personal key there for your script.

### Actions only Console takes in live [#actions-only-console-takes-in-live]

Some live actions need a person signed in to Console, whatever keys exist:

* Approving or rejecting a promotion.
* Applying a member import.
* Creating a personal key for live, or changing which trackers one may send to.

Deleting a whole quest's evidence at once in live takes an organization admin, through Console or their own personal key, or a service key holding Review, because it can't be undone. Deleting one attempt's evidence stays a reviewer's action.

> [!DEV]
> A definition write in live answers 422 `promotion-required` to Console sessions and to personal, service and hosted app keys; a CLI session, AI assistant connection or automation key mostly gets 403 `console-approval-required` from the live limits first. Deciding a promotion, applying a member import, and publishing, rejecting or rolling back a live code deployment made before promotions answer 403 `console-approval-required` in live to any key; a CLI session, AI assistant connection or automation key gets the same code for its other live writes. An automation key or AI assistant connection also can't attach, re-check or remove a custom domain from test, because a domain always serves live. The quest-wide evidence deletion is `POST /management/v1/attempts/redact-evidence`; in live it answers 403 `permission-required` to any other credential the live limits above let through.
