# Scopes

Every operation requires exactly one scope, named `<resource>.<action>`:

```
shifts.read        locations.write        timesheets.read
```

- `GET` requires `.read`.
- `POST`, `PATCH`, `DELETE` require `.write`.
- **Write implies read** — `shifts.write` also satisfies `shifts.read`, so you never need to request both.

## The catalog

The grantable set is closed and shipped as part of the contract. Here it is in full — one row per resource, each with its `.read` and `.write` scope:

```{include} _scopes-table.md
```

For a single endpoint you don't need this table: **every operation in the <a href="../../api/index.html">API Reference</a> declares the exact scope it requires**, right on the operation. Both that requirement and the table above are derived from the same value the server's permission check reads, so what is documented and what is enforced cannot drift apart.

## Asking for the right ones

Scopes are granted per integration client when we register it, so ask for what the integration actually does and nothing more. A payroll export that reads hours gets `timesheets.read` and nothing else.

A typo is caught at registration, not at runtime: `shift.read` is refused with a suggestion rather than registering a client that fails on every call.

To add a scope later, email {{ support_email }} — it does not require new credentials.

## When a scope is missing

A call with a valid token but without the required grant is:

```json
{
  "error": {
    "type": "permission_error",
    "code": "insufficient_scope",
    "message": "…",
    "details": []
  }
}
```

with status **403**. That is distinct from a `401` (the token itself is bad — see {doc}`authentication`) and from a `404`, which is also what you get for a record outside your company, so that existence never leaks.
