> ## Documentation Index
> Fetch the complete documentation index at: https://docs.draftliftai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Key Scopes & Workspace Binding

> Pick the right scopes and decide whether to bind an API key to a workspace.

# API Key Scopes & Workspace Binding

Every DraftLift API key carries two governance fields beyond its name:

1. **Scopes** — what the key can do (`read`, `write`).
2. **Workspace binding** — which workspace the key operates against (a specific workspace, or all of them).

Both are chosen when you create the key in **Settings → API Keys** and cannot be changed after issue. Re-issue the key if you need different settings.

## Scopes

| Scope   | What it allows                                                       |
| ------- | -------------------------------------------------------------------- |
| `read`  | List, get, search — every `GET` endpoint.                            |
| `write` | Everything `read` allows, plus create, update, delete, and generate. |

Scopes are additive. A key with `["read", "write"]` is the most permissive issuable today; a `read`-only key is read-only across the entire API.

The `admin` scope is reserved in the schema but is not surfaced in the UI. v1 keys cannot manage other API keys, billing, or admin endpoints — those operations always require an interactive web session and are denylisted on the public API. See [Authentication](/api-reference/authentication) for the full denylist.

### When to issue a read-only key

Use `read`-only keys for:

* Reporting dashboards, analytics jobs, or anything that only consumes existing content.
* Long-lived background services that should never mutate state if they get compromised.
* Sharing data with third-party tooling.

### When to include `write`

Use `read + write` keys for:

* AI agents that generate, edit, score, or schedule content.
* CI/CD pipelines that publish or convert drafts.
* The CLI on a developer laptop.

The default in the create dialog is `read + write` because most CLI/MCP usage requires both. Untick **Write** to harden a key meant for read-only consumers.

### Insufficient scope errors

A scope-gated mutation called with a `read`-only key returns:

```http theme={null}
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "FORBIDDEN",
  "message": "This endpoint requires the 'write' scope.",
  "details": {
    "error_code": "insufficient_scope",
    "required_scope": "write",
    "current_scopes": ["read"],
    "upgrade_action": "Re-issue your API key with the required scope at https://app.draftliftai.com/settings/api-keys"
  },
  "trace_id": "tr_..."
}
```

The CLI maps this to a typed `InsufficientScopeError` with exit code 2 and prints the `upgrade_action` URL on its own line.

## Workspace binding

Workspaces partition references, memories, content calendars, and generated drafts. A workspace is identified by its UUID (`ws_...`).

When you create a key, you choose one of two postures:

| Posture                  | When to use                                                                                          |
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
| **All workspaces**       | Multi-workspace agents, CLI on a developer laptop, anything that needs to operate across workspaces. |
| **Bound to a workspace** | Per-client keys, scoped CI tokens, or any key you want to silently lock to one workspace's data.     |

If you have exactly one workspace, the create dialog hides the workspace picker and binds the key to that workspace automatically.

### Behavior at request time

| Request                                            | Outcome                                                                                                                              |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Unbound key, no `workspace_id`                     | Server resolves nothing; endpoints that require a workspace return `400` (or operate on the user's default, depending on the route). |
| Unbound key, explicit `workspace_id`               | Operates on the requested workspace.                                                                                                 |
| Bound key, no `workspace_id`                       | Server injects the bound workspace automatically (200 OK).                                                                           |
| Bound key, `workspace_id` matches the binding      | 200 OK.                                                                                                                              |
| Bound key, `workspace_id` differs from the binding | `403 workspace_mismatch`. The server never silently overrides.                                                                       |

### Workspace mismatch errors

```http theme={null}
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": "FORBIDDEN",
  "message": "This API key is bound to a specific workspace.",
  "details": {
    "error_code": "workspace_mismatch",
    "bound_workspace_id": "ws_aaa...",
    "requested_workspace_id": "ws_bbb..."
  },
  "trace_id": "tr_..."
}
```

The CLI surfaces both IDs in a `WorkspaceMismatchError` (exit code 2) and suggests either re-issuing the key without a binding or running with `--workspace <bound>`.

## Choosing settings for common scenarios

| Scenario                                                  | Scopes         | Workspace binding                |
| --------------------------------------------------------- | -------------- | -------------------------------- |
| Agent producing content for one client                    | `read + write` | Bound to that client's workspace |
| Internal CI that converts blog → social across all brands | `read + write` | Unbound                          |
| Read-only metrics dashboard                               | `read`         | Unbound (or bound, if scoped)    |
| Personal CLI on a laptop                                  | `read + write` | Unbound                          |
| Third-party integration that only fetches content         | `read`         | Bound to a "public" workspace    |

## Rotation and expiry

When you create a key, pick an expiration: **30 days**, **90 days** (default), **365 days**, or **Never**. The default is 90 days because it forces a yearly-quarterly rotation cadence without being onerous; pick `Never` only for keys you actively monitor in audit logs.

To rotate:

1. Create a new key with the same scopes and binding.
2. Roll the key into your config or secret store.
3. Revoke the old key from **Settings → API Keys**.

Revocation takes effect immediately — in-flight requests using the old key will start receiving `401 Unauthorized` within seconds.

## Introspecting a key

You can check what a key can do at any time:

```bash theme={null}
dl whoami --json
```

Or call the introspection endpoint directly:

```bash theme={null}
curl -H "Authorization: Bearer dl_live_..." \
  https://draftliftai.com/api/v1/api-keys/current
```

This returns the calling key's `name`, `scopes`, `workspace_id`, `expires_at`, and `last_used_at`. It is the only `/api-keys/*` endpoint that accepts `dl_live_` keys — every other key-management operation requires a web session.

## Next steps

<CardGroup cols={2}>
  <Card title="CLI Quickstart" icon="terminal" href="/quickstart/cli">
    Walk through `dl login`, references, and your first generation.
  </Card>

  <Card title="Authentication" icon="lock" href="/api-reference/authentication">
    Full reference for keys, headers, error shapes, and the denylist.
  </Card>
</CardGroup>
