# API keys and scopes

An API key lets a script, CI job or another system call the EvoHub API without a person signing in. Each key carries **scopes**: the exact permissions it has. This page explains the two kinds of key, how to create and revoke them, and their limits.

## Two kinds of key

| | Personal key | Organization key |
| --- | --- | --- |
| Where | **My Account → API Keys** | **Organization → Organization Keys** |
| Who can create one | Anyone signed in | People with the **Organization Keys → Write** permission (Owners and Admins by default) |
| What it can do | The scopes you pick, never more than you hold yourself | Exactly what its role grants, as the role changes over time |
| How long it lives | Stops working when you leave the organization or delete your account | Belongs to the organization and outlives the people who manage it |
| Best for | Your own scripts and experiments | CI pipelines and integrations that must keep running |

Both kinds work in one organization only, and both use the same headers. See [API overview](https://docs-dev.evohub.io/api-overview.md#authentication).

## Create a personal key

:::steps
### Open API Keys
Open the avatar menu, choose **My Account**, then **API Keys**, and select **Create Key**.

### Name it
Enter a **Name** that says what the key is for, for example "Deploy pipeline". The name is the only way to tell keys apart later.

### Pick the organization
Under **Organization**, choose which of your organizations the key works in.

### Choose an expiry
Under **Expires**, pick **90 days** (the default), **180 days**, **1 year** or **Never expires**.

### Choose scopes
Tick the permissions the key needs. They are grouped by product, as in the role editor. Anything you do not hold yourself in that organization is greyed out. Selecting a write scope also selects the read it depends on.

### Copy the key
Select **Create Key**. The key is shown once, in the **Copy your key now** dialog. Copy it and store it in your secret manager, then select **I have copied it**.
:::

> [!WARNING]
> EvoHub stores only a hash of each key, so a key cannot be shown again or recovered. If you lose it, revoke it and create a new one.

A key with no scopes can authenticate but is refused everywhere. That is the safe default, not an unrestricted key.

A personal key is always a slice of you, measured at the moment it is used. If your own permissions shrink, for example because an administrator changed your role, the key loses those permissions too. If you leave the organization or delete your account, the key stops working.

## Create an organization key

:::steps
### Open Organization Keys
Go to **Organization → Organization Keys** and select **Create Key**.

### Name it
Enter a **Name**, for example "GitHub Actions — deploy".

### Pick a role
Under **Role**, choose the role whose permissions the key should have. You can only pick a role whose permissions you hold yourself, and only an administrator can give a key the Admin role. Create a [custom role](https://docs-dev.evohub.io/roles-and-permissions.md#custom-roles) that holds exactly what the integration needs.

### Choose what it sees
Under **Sees**, keep **Whole organization** or pick a team. A key scoped to a team sees that team's boards, schedules and monitors, the same view a member of that team has.

### Choose an expiry and copy the key
Pick an expiry under **Expires**, select **Create Key**, and copy the key from the dialog. As with personal keys, it is shown only once.
:::

An organization key's scopes are its role's, read live. Edit the role and every key on it changes; delete the role and every key on it stops working.

## Scopes

Scopes are the same permissions roles are built from. They are grouped by product:

| Product | Scopes |
| --- | --- |
| Organization | `identity:user:read`, `identity:user:write`, `identity:user:invite`, `identity:team:read`, `identity:team:write`, `identity:role:read`, `identity:role:write`, `identity:org:write`, `identity:apikey:read`, `identity:apikey:write`, `identity:audit:read` |
| On-Call | `oncall:alert:read`, `oncall:alert:write`, `oncall:alert:respond`, `oncall:incident:read`, `oncall:incident:write`, `oncall:schedule:read`, `oncall:schedule:write`, `oncall:escalation:read`, `oncall:escalation:write`, `oncall:integration:read`, `oncall:integration:write`, `oncall:maintenance:read`, `oncall:maintenance:write`, `oncall:postmortem:read`, `oncall:postmortem:write`, `oncall:settings:write`, `oncall:audit:read` |
| Uptime | `uptime:monitor:read`, `uptime:monitor:write`, `uptime:incident:read`, `uptime:silence:read`, `uptime:silence:write`, `uptime:channel:read`, `uptime:channel:write`, `uptime:audit:read` |
| Status pages | `status:page:read`, `status:page:write`, `status:component:read`, `status:component:write`, `status:incident:read`, `status:incident:write`, `status:maintenance:read`, `status:maintenance:write`, `status:subscriber:read`, `status:subscriber:write`, `status:audit:read` |
| Retro | `retro:board:read`, `retro:board:write`, `retro:board:delete`, `retro:card:read`, `retro:card:write`, `retro:action:read`, `retro:action:write`, `retro:audit:read` |
| Board | `board:board:read`, `board:board:write`, `board:board:delete`, `board:list:read`, `board:list:write`, `board:card:read`, `board:card:write`, `board:member:write`, `board:review:write` |
| Docs | `docs:site:read`, `docs:site:write`, `docs:site:delete`, `docs:page:write`, `docs:page:publish` |
| Changelog | `changelog:site:read`, `changelog:site:write`, `changelog:site:delete`, `changelog:entry:write`, `changelog:entry:publish` |
| Billing | `billing:usage:read`, `billing:invoice:read`, `billing:payment:read`, `billing:payment:write`, `billing:profile:read`, `billing:profile:write`, `billing:credit:write`, `billing:commitment:write` |
| Support | `support:audit:read` |

What each permission covers is described in [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md#permissions-reference).

Give a key the fewest scopes it needs. A key that only reads monitors needs `uptime:monitor:read` and nothing else.

## What a key can never do

API keys are for automation, and some actions are reserved for a person signed in to the console.

- **A key never counts as an administrator.** Owners and Admins pass every check when they act in person, but a key is held to its scopes, even if it was created by an Admin or carries the Admin role. A key never sees another team's work just because its creator could.
- **A key cannot approve or review.** Approving or requesting changes on docs change requests and changelog drafts, and merging or reverting a docs change request, must be done by a person. So must connecting a docs site to GitHub and issuing access credentials for a private docs site. A key that tries gets 403 `PERSON_REQUIRED`.
- **A key cannot act on a person's own account.** It cannot switch organizations, create an organization, change a profile or password, manage sessions, two-factor authentication or connected accounts, accept invitations, or delete an account. These return 403 `API_KEY_NOT_ALLOWED`.
- **A key cannot manage API keys.** Creating, listing and revoking keys, of either kind, is done in the console.
- **An organization key cannot send invitations**, because an invitation names the person who sent it. Use a personal key with `identity:user:invite`.

## Revoke a key

Revoking stops a key immediately, and anything still using it starts failing at once. It cannot be undone.

- **Personal key:** in **My Account → API Keys**, select **Revoke** on the key's row and confirm. You can revoke your own keys in any organization.
- **Organization key:** in **Organization → Organization Keys**, select **Revoke** on the key's row and confirm. This needs the **Organization Keys → Write** permission.

Your **My API Keys** list shows each key's prefix, organization, scopes (hover the count to see them), state (**active**, **revoked** or **expired**), when it was last used and when it expires. A key that has never been used shows **Never** under **Last used**, which makes unused keys easy to find and revoke.

> [!TIP]
> If a key leaks, for example into a public repository or a log, revoke it first and then create its replacement. Because every key starts with `evohub_`, secret scanners can be configured to spot it.

## Related

- [API overview](https://docs-dev.evohub.io/api-overview.md)
- [Errors](https://docs-dev.evohub.io/errors.md)
- [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md)
- [Security at EvoHub](https://docs-dev.evohub.io/security.md)
