---
title: "API keys"
description: "Credentials for automation, scoped to the permissions you grant."
url: "https://saved.sh/docs/cli/api-keys"
---

An API key is a machine credential for a workspace, carrying a subset of the
[permission catalog](/docs/security/permissions#the-catalog).

Use one in CI rather than your own session: it is scoped to exactly what you grant, and
revoking it does not sign you out.

## Creating [#creating]

```bash
sctl apikey create ci-backups --perms backups:read,backups:write
```

```
Created key "ci-backups" (7c1e...).

  Secret (copy now, not shown again):
  sk_live_...

Permissions: backups:read, backups:write
```

`--perms` is required and takes a comma-separated list of permission slugs. The command is
also available as `sctl key`.

<Callout type="error">
  The secret is shown **once** and is never stored by us. We keep the key's identifier so we
  know which key is calling. If you lose it, create a new key and revoke the old one.
</Callout>

## Listing and revoking [#listing-and-revoking]

```bash
sctl apikey list
sctl apikey revoke <key-id>
```

```
NAME          ID        PERMISSIONS
ci-backups    7c1e...   backups:read,backups:write
monitoring    9a3f...   backups:read,artifacts:read
```

## Choosing permissions [#choosing-permissions]

Grant the narrowest set that does the job. The distinction that matters most:

| Permission           | Lets the key                                    |
| -------------------- | ----------------------------------------------- |
| `backups:read`       | See that a backup exists, and its configuration |
| `backups:write`      | Create, configure, trigger, pause and delete    |
| `artifacts:read`     | See artifacts, sizes, checksums and key IDs     |
| `artifacts:download` | **Read your data**                              |

A monitoring key needs `backups:read` and `artifacts:read`. It does not need
`artifacts:download`, and giving it that turns a monitoring credential into an exfiltration
one.

Shapes worth copying:

| Purpose                         | `--perms`                                        |
| ------------------------------- | ------------------------------------------------ |
| Apply manifests from CI         | `backups:read,backups:write`                     |
| Trigger a backup after a deploy | `backups:read,backups:write`                     |
| Monitor without touching        | `backups:read,artifacts:read`                    |
| Restore automation              | `backups:read,artifacts:read,artifacts:download` |

## What a key cannot hold [#what-a-key-cannot-hold]

Some permissions are refused on any machine credential, whatever you ask for.

| Refused              | Why                                                                                     |
| -------------------- | --------------------------------------------------------------------------------------- |
| `workers:*`          | A compromised key could mint worker credentials                                         |
| `api-keys:*`         | One leaked key could mint more, and revoking the original would not revoke what it made |
| `billing:*`          | The workspace's cards                                                                   |
| `destinations:write` | Linking a bucket hands over a long-lived S3 key pair                                    |
| `connections:write`  | The OAuth flow ends at a browser consent screen a machine cannot complete               |
| `artifacts:confirm`  | Worker-only. That is a worker's signal about its own work                               |

Asking for one fails with `422` naming the slug. Creating a workspace is also refused, since
that requires a human principal.

## Using a key [#using-a-key]

There is no separate mode. The key is a bearer credential exactly like a session token, so it
goes where the session token would:

```bash
export SCTL_ACCESS_TOKEN='sk_live_...'
export SCTL_WORKSPACE_ID='4b7e1d90-...'
sctl backup list
```

```powershell
$env:SCTL_ACCESS_TOKEN = 'sk_live_...'
$env:SCTL_WORKSPACE_ID = '4b7e1d90-...'
sctl backup list
```

**Both variables are required.** A machine never ran `sctl workspace switch`, so nothing has
told it which workspace to act on. `SCTL_WORKSPACE_ID` comes from `sctl workspace list`.

Or call the API directly:

```bash
curl -fsS https://api.saved.sh/v1/backups \
  -H "Authorization: Bearer $SCTL_ACCESS_TOKEN"
```

## Storing one [#storing-one]

| Do                                          | Do not                |
| ------------------------------------------- | --------------------- |
| GitHub Actions secrets, GitLab CI variables | Commit it             |
| Vault, AWS Secrets Manager, 1Password       | Bake it into an image |
| An environment variable read at runtime     | Echo it in CI output  |

<Callout type="warn">
  On Linux and macOS, a leading space before `export` keeps the line out of shell history in
  most shells. Better still, read the key from a secrets manager rather than typing it.
</Callout>

## Rotation [#rotation]

Zero-downtime order:

1. Create the new key with the same permissions.
2. Deploy it everywhere the old one is used.
3. Confirm traffic works on the new key.
4. Revoke the old one.

```bash
sctl apikey create ci-backups-2026-08 --perms backups:read,backups:write
# deploy, verify, then:
sctl apikey revoke <old-key-id>
```

<Callout type="warn">
  Revocation is **not instantaneous**. Validations are cached for about a minute, so a revoked
  key can keep working briefly. Treat it as effective within a minute, and if the key leaked,
  also rotate anything it could reach.
</Callout>

## Keys outlive people [#keys-outlive-people]

A key belongs to the workspace, not to whoever created it, so removing a member does not
revoke their keys. That keeps automation running when someone leaves, and it means offboarding
has a second step:

```bash
sctl apikey list
```

Name keys after their **use** rather than their creator (`ci-deploy`, `monitoring`, not
`sams-key`), so that review is possible at all.

## Limits [#limits]

| Plan  | API keys per workspace |
| ----- | ---------------------- |
| Trial | 2                      |
| Paid  | 20                     |

## Next [#next]

<Cards>
  <Card href="/docs/cicd/api-keys" title="CI/CD" description="Using a key from a pipeline." />

  <Card href="/docs/security/api-keys" title="API key security" description="The design, and what a leaked key can do." />

  <Card href="/docs/security/permissions" title="Permissions" description="The full catalog." />
</Cards>
