# API keys and scopes

> How an up4 API key is created, sent and checked: the Bearer header, the scope each route needs, read-only keys, rotation and the errors you get when something is missing.

Source: https://upfour.io/docs/api/keys-and-scopes

An API key is a Bearer token that acts as the person who created it. Each key carries scopes, and every route names the one scope it needs. Keys are created in the app and never by the API itself.

## Create a key

In the app: **Settings → API tokens**. The form asks for a name, an access level (read-only or read and write), an expiry (never, 30, 90 or 365 days; the form starts at 90), the scopes and, when agency mode is on, the clients the key may see.

![API token form with the name CI integration, access level Read-only, and monitor.read and monitor.write checked.](https://upfour.io/docs/images/api-tokens.webp)

**Create a scoped token.** Name, access level and permissions in the token form.

1. **Token name** Choose a name that identifies the integration, such as CI integration.
2. **Access level** The default for a token without explicit scopes. Read-only is the safe start. The screenshot shows the form before a token is created.
3. **Scopes** Select only the operations the integration needs. The example allows reading and writing monitors.
- The key is shown once. up4 stores only a SHA-256 hash, so a lost key cannot be shown again; create a new one.
- Every key starts with `pls_`.
- Without an explicit scope list, a read-only key gets every `.read` scope and a read and write key gets every read, write and delete scope.
- A key cannot create, rotate or revoke keys. Those routes answer only to a signed-in browser session.
- **Rotate** in the app issues a new key with the same name, scopes, access level, clients and expiry. The old key keeps working for at most 24 more hours, so you can switch over without downtime.
- up4 records when a key was last used.

## Send the key

Put it in the `Authorization` header as a Bearer token. `GET /me` needs only `self.read` and shows what the key can do:

```bash
export UP4_API_KEY="pls_…"

curl -s https://app.upfour.io/v1/me \
  -H "Authorization: Bearer $UP4_API_KEY"
```

```json
{
  "data": {
    "api_key_id": "…",
    "api_version": "2026-05-07",
    "auth_method": "api_key",
    "client_set": { "All": true, "CompanyLevel": true, "IDs": null },
    "org_id": "…",
    "org_role": "owner",
    "scopes": ["self.read", "monitor.read", "monitor.write", …],
    "user_id": "…"
  }
}
```

`scopes` comes in no fixed order. A team key also shows `team_id`. `client_set` is explained on the [client scope](https://upfour.io/docs/api/client-scope.md) page.

## Scopes

A scope is `<resource>.read`, `.write` or, for monitors, `.delete`. Each reference page states the scope per operation; the table shows what the families cover.

| Read scope | Write scopes | Covers |
| --- | --- | --- |
| `monitor.read` | `monitor.write, monitor.delete` | Monitors and monitor groups. Write covers create, update, pause, resume, check now, bulk and batch changes; delete is its own scope. |
| `heartbeat.read` | `heartbeat.write` | Scheduled jobs (heartbeats) and their groups. Deleting needs write. |
| `incident.read` | `incident.write` | Incidents, severities and severity groups. |
| `status_page.read` | `status_page.write` | Status pages, update templates and design templates. Subscriber changes need status_page.subscriber.write. |
| `notification_channel.read` | `notification_channel.write` | Alert channels. |
| `escalation_policy.read` | `escalation_policy.write` | Escalation policies. |
| `oncall.read` | `oncall.write` | On-call schedules, rotations and overrides. |
| `runbook.read` | `runbook.write` | Playbooks (runbooks). |
| `client.read` | `client.write` | Clients and client templates. |
| `team.read` | `team.write` | Teams. |
| `member.read` | `member.write` | Company members and invites, and the members of a team. |
| `report.read` | `report.write` | Reports, report schedules and share links, SLA policies and the advisor. |
| `webhook.read` | `webhook.write` | Webhook subscriptions and their deliveries. |
| `metadata.read` | `metadata.write` | Metadata on monitors, scheduled jobs, incidents and clients. |
| `organization.read` | `organization.write` | Company settings: branding, security, SSO. |
| `audit_log.read` | `—` | The audit log. |
| `self.read` | `—` | GET /me, the key introspecting itself. |

You may also see `api_key.read` and `api_key.write` on a key. No public route needs them.

A key that is read-only by access level is refused on every write route, even if its scope list contains a write scope. The role of the person who created the key applies as well: a viewer cannot write with a read and write key.

## When the key is refused

| Status | error.code | error.message | Meaning |
| --- | --- | --- | --- |
| 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid authorization header format | The header is present but is not Bearer followed by a token. The scheme is case-sensitive: write Bearer. |
| 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | API key required | The token does not start with pls\_. |
| 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid API key | The key is unknown, revoked or expired, or its Company was deleted. |
| 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Session cookie required | There is no Authorization header at all, so the request is treated as a browser request. |
| 401 | [unauthorized](https://upfour.io/docs/api/errors#code-unauthorized) | Invalid organization | The person who created the key is no longer a member of the Company. |
| 403 | [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) | API token does not have the required scope | The key lacks the scope this route needs. details.required_scope names it. |
| 403 | [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope) | API token cannot access this route | The route is not part of the public API. |
| 403 | [read_only_api_key](https://upfour.io/docs/api/errors#code-read_only_api_key) | Read-only API keys cannot use write permissions | The key has the read-only access level and the route needs a .write or .delete scope. |
| 403 | [api_key_scopes_required](https://upfour.io/docs/api/errors#code-api_key_scopes_required) | Legacy API key has no explicit scopes and must be rotated | An old key without a scope list. Rotate it in the app. |
| 403 | [session_required](https://upfour.io/docs/api/errors#code-session_required) | This route is available only to an authenticated browser session | The route belongs to the app: key management, billing, user settings, the dashboard. |

The shape of these bodies is on the [responses](https://upfour.io/docs/api/responses.md) page; every code is in the [error catalogue](https://upfour.io/docs/api/errors.md), for example [insufficient_scope](https://upfour.io/docs/api/errors#code-insufficient_scope).

Company-level routes such as `/organization` answer `404 Not found` to team keys and to keys that cannot read company-level resources.

## Common mistakes

- Writing `bearer` in lowercase, or leaving out the word. Both give `401`.
- Reading `403` as a bad key. A bad key is `401`; `403` means the key is valid but lacks the scope or the write access.
- Creating a key as read-only and adding write scopes later in your head. Create a read and write key, or rotate to one.
- Keeping keys in shared chat or in code. Revoke a leaked key in the app; it stops working at once.
- Using a key tied to one person for an integration. When that person leaves the Company, the key answers `401`.
