# Access tokens & API keys

Every `/v1` call carries `Authorization: Bearer <credential>`. There are two
kinds of credential:

| Credential | Belongs to | Use it for | Create it in the console |
|---|---|---|---|
| Personal access token | You | Your own CLI and scripts | **Account → Access tokens** |
| Organization API key | An organization | CI, deploy bots, agents | **Organization → API tokens** |

Both secrets are shown exactly once at creation; Scribase stores only a SHA-256
digest and a short display prefix. Revoke a credential the moment it may have
leaked.

---

## Personal access tokens

Personal access token routes are authenticated by your own session or an
existing token.

### GET /v1/account/tokens

Lists your tokens (metadata only, never the secret).

```json
{
  "tokens": [
    {
      "id": "3f2c...",
      "name": "laptop",
      "token_prefix": "scb_pat_1a2b3c4d",
      "created_at": 1790000000,
      "last_used_at": 1790050000,
      "expires_at": 1797776000,
      "revoked_at": null
    }
  ]
}
```

### POST /v1/account/tokens

Creates a token. **Headers required:** `Content-Type: application/json`,
`Idempotency-Key`.

| Field | Type | Notes |
|---|---|---|
| `name` | string | 1–200 characters |
| `expires_in_days` | integer, optional | Omit for a non-expiring token |
| `ttl` | string, optional | Instead of `expires_in_days`: `30m`, `1h`, `24h`, `7d`, `30d`, `never` |
| `scopes` | string array, optional | Makes it an **agent token** (`scb_agt_…`) limited to these scopes; see [Tokens and safety](https://docs.scribase.com/docs/agents/safety.md). Without `ttl` it expires after 8 hours. Its `token` starts with a readable scope and expiry summary (`scb_agt_1p-rw-sqlw-x2609260412_…`) |
| `organization_id` | string, optional | Binds an agent token to one organization; required when a scope names a specific project |

The response and the list include `kind` (`personal` or `agent`), `scopes`
and `organization_id`. `GET /v1/account/tokens/{id}/events` returns the last
200 requests an agent token made (`method`, `path`, `status`, `client_ip`,
`at`).

```sh
curl -X POST "$SCRIBASE_API_URL/v1/account/tokens" \
  -H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: create-ci-token-001' \
  -d '{"name": "ci", "expires_in_days": 90}'
```

**Response `201 Created`** — the only time `token` is returned:

```json
{
  "id": "7d1e...",
  "name": "ci",
  "token": "scb_pat_...",
  "token_prefix": "scb_pat_9f8e7d6c",
  "created_at": 1790000000,
  "expires_at": 1797776000
}
```

An agent token's response also carries `agent_message` and `revoke_url`.
`agent_message` is the text the console's "Copy for your AI agent" button
copies. It tells the agent what the string is, so the agent uses it instead of
refusing it:

```text
Scribase agent token (scoped to project web, read/write, SQL read/write,
migrations, no secret keys, expires in 8h, revocable at
https://console.scribase.com/account/tokens). Designed to be shared with coding
agents: scb_agt_1p-rw-sqlw-mig-x2609260412_...
```

Coding agents should not need a token created here at all: they sign in with
the [device flow](https://docs.scribase.com/docs/api/device-login.md) (`scribase login`), which a person
approves in the browser.

### DELETE /v1/account/tokens/{token_id}

Revokes a token immediately. **Headers required:** `Idempotency-Key`.

---

## Organization API keys

Creating and revoking keys requires the `owner` or `admin` role in the
organization.

### GET /v1/organizations/{org}/api-keys

```json
{
  "items": [
    {
      "api_key_id": "key_4c1d...",
      "organization_id": "acme",
      "name": "deploy-bot",
      "scopes": ["deploy"],
      "created_at_epoch_seconds": 1790000000,
      "last_used_epoch_seconds": 1790050000
    }
  ]
}
```

### POST /v1/organizations/{org}/api-keys

| Field | Type | Notes |
|---|---|---|
| `name` | string | 1–120 characters |
| `scopes` | string[] | Non-empty subset of `read`, `deploy`, `admin` |

```sh
curl -X POST "$SCRIBASE_API_URL/v1/organizations/acme/api-keys" \
  -H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "deploy-bot", "scopes": ["deploy"]}'
```

**Response `201 Created`** includes the `secret` (prefixed `sbk_`) once.

### DELETE /v1/organizations/{org}/api-keys/{api_key_id}

Revokes a key. **Headers required:** `Idempotency-Key`.

---

## SDK

```ts
const created = await scribase.account.createToken({ name: 'ci', expires_in_days: 90 });
await scribase.account.revokeToken(created.id);

const key = await scribase.organizations.createApiKey('acme', { name: 'deploy-bot', scopes: ['deploy'] });
await scribase.organizations.revokeApiKey('acme', key.api_key_id);
```

## Using a credential

```sh
export SCRIBASE_API_URL=https://api.scribase.com
export SCRIBASE_ACCESS_TOKEN=scb_pat_...
scribase doctor
```
