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

# Organizations & API tokens

> How access is scoped to organizations and roles, and how org API tokens are minted, used, and revoked.

Every project in astilba belongs to an **organization**, and every request is scoped to one
org resolved from your credential — never from the request itself. Within an org, what you can
do depends on your **role**, and programmatic access uses **org API tokens**.

## Organizations and roles

Authentication and organizations are handled by [Better Auth](https://www.better-auth.com).
You belong to one or more organizations; a freshly signed-in user's **active organization**
defaults to their first membership, so they can immediately work in it.

Three roles matter for the parts astilba exposes today:

| Role       | Can                                                                 |
| ---------- | ------------------------------------------------------------------- |
| **owner**  | everything, including minting and revoking API tokens               |
| **admin**  | manage projects and translations; mint and revoke API tokens        |
| **member** | read and edit translations; **list** tokens, but not mint or revoke |

<Note>
  Roles are enforced **on the server**. The dashboard hides controls you can't use as a
  convenience, but the API is the real gate — a request without the role is rejected
  regardless of what UI made it.
</Note>

## API tokens

An **org API token** authenticates a non-interactive client — the [CLI](/reference/cli), CI, or
any HTTP caller. It carries the org it was minted in, so it reaches **every project in that org
by slug** (another org's slug returns `404`). It is sent as the **`x-api-key`** header; the CLI
reads it from `ASTILBA_TOKEN` (or `--token`).

### Minting

Mint a token from the dashboard's **Tokens** page (owner/admin only). The plaintext key is
shown **exactly once** — only its hash is stored, so it can never be displayed again. Copy it
immediately.

Programmatically, that's `POST /api/tokens`, which requires a **session cookie** (you manage
tokens from the dashboard, not with another token). The raw Better Auth key routes
(`/api/auth/api-key/*`) are deliberately blocked so token management always goes through the
role-checked `/api/tokens` endpoints.

### Using a token

```sh theme={null}
export ASTILBA_URL=https://astilba.example.com
export ASTILBA_TOKEN=…
npx astilba pull --project web ./locales
```

Or as a raw header:

```sh theme={null}
curl -H "x-api-key: $ASTILBA_TOKEN" \
  https://astilba.example.com/api/projects/web
```

### Revoking

Revoke from the **Tokens** page, or `DELETE /api/tokens/:id` (owner/admin, session-cookie).
Revocation is immediate. Mint a separate token per CI system or developer so you can revoke one
without disrupting the others.

## Access-control matrix

| Operation                | Endpoint                 | Who                               |
| ------------------------ | ------------------------ | --------------------------------- |
| List tokens              | `GET /api/tokens`        | any member                        |
| Mint a token             | `POST /api/tokens`       | owner / admin                     |
| Revoke a token           | `DELETE /api/tokens/:id` | owner / admin                     |
| Read / edit translations | `/api/projects/*`        | any member (session **or** token) |

See the [Server HTTP API](/reference/server-api) for the full endpoint list and status codes.
