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

# Authentication

> Repo uses bearer-token API keys. One key per agent, scoped by action and provider.

## Bearer tokens

Every Repo API request requires an `Authorization` header:

```http theme={null}
Authorization: Bearer repo_live_a1b2c3...
```

Keys start with the `repo_` prefix and are issued from the console's **Developers** tab. The full secret is shown **once** at creation time — Repo stores only a SHA-256 hash, so a lost key cannot be recovered. Mint a new one and rotate.

## Two ways to mint keys

| Method              | Who                                     | When                                                        |
| ------------------- | --------------------------------------- | ----------------------------------------------------------- |
| Console UI          | Humans with a Supabase login            | First key for an org, manual rotation                       |
| `POST /v1/api-keys` | An existing key with the `admin` action | Programmatic key creation from CI/CD or your own admin tool |

The bootstrapping pattern: mint your first admin key in the console, store it in your secrets manager, then use the API to issue scoped agent keys.

## Key shape

```json theme={null}
{
  "id": "uuid",
  "name": "support-agent-prod",
  "prefix": "repo_a1b2",
  "actorType": "agent",
  "allowedActions": ["search", "context", "ask", "memory:read"],
  "allowedProviders": ["slack", "notion"],
  "lastUsedAt": "2026-05-30T20:14:00Z",
  "createdAt": "2026-05-23T18:33:00Z"
}
```

### Actor types

* **`agent`** — Default. An AI agent making programmatic requests.
* **`application`** — A traditional application or backend service.
* **`admin`** — Capable of minting other keys (`admin` action grants this).

The actor type is metadata only — it doesn't change what the key can do (that's controlled by `allowedActions`). It's there for audit + observability.

## Actions

Every action is a discrete permission a key may carry. Pass the full set when creating a key; Repo enforces them at the route level.

| Action          | Endpoint(s)                                | Purpose                                                  |
| --------------- | ------------------------------------------ | -------------------------------------------------------- |
| `admin`         | `/v1/api-keys` (CRUD), `/v1/maintenance/*` | Mint/revoke keys, run maintenance jobs                   |
| `search`        | `POST /v1/search`                          | Vector search across the org's memory                    |
| `context`       | `POST /v1/context`                         | Full Context Contract (search + citations + exclusions)  |
| `ask`           | `POST /v1/ask`                             | Grounded answer generation with citations                |
| `memory:read`   | `GET /v1/memory-canvas`                    | Graph view of entities + relationships                   |
| `sources:read`  | `GET /v1/sources`                          | List connectors                                          |
| `sources:write` | `PATCH /v1/sources/:id`                    | Modify connector settings (e.g. Slack channel allowlist) |
| `sync:read`     | `GET /v1/sync-runs`                        | View sync run history                                    |
| `sync:write`    | `POST /v1/sync-runs`                       | Queue a sync run                                         |
| `ingest`        | `POST /v1/ingest`                          | Push source documents directly (no connector required)   |

## Provider scopes

`allowedProviders` is an optional whitelist of provider IDs the key may retrieve from. Pass `null` for no restriction (the key sees everything the org has connected); pass an array to scope it down.

```json theme={null}
{
  "allowedProviders": ["slack", "notion"]
}
```

Available providers: `slack`, `google_drive`, `notion`, `gmail`.

When `/v1/search` or `/v1/context` runs against a key with provider scopes, hits from excluded providers are filtered out *server-side* before any data leaves Repo — the key cannot bypass the scope by inspecting the response.

## Rate limits

Each `(api_key, action)` pair has a per-minute budget — default 60 req/min, configurable per-deployment via `API_RATE_LIMIT_PER_MIN`. Over-limit calls return `429` with headers:

```http theme={null}
HTTP/1.1 429 Too Many Requests
Retry-After: 47
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-05-30T20:15:00Z
```

See [Rate limits](/rate-limits) for tier-specific budgets and burst behavior.

## Revocation

```bash theme={null}
curl -X DELETE https://api.userepo.com/v1/api-keys/{id} \
  -H "Authorization: Bearer repo_admin_key"
```

Revoked keys take effect immediately (no cache). The revoke endpoint refuses to delete the key currently authenticating the request — you can't lock yourself out by accident.

## Console keys vs. API keys

The **console** uses Supabase Auth tokens (the `Authorization: Bearer eyJ...` from a logged-in human). API keys are for agents. Repo treats them as different actor types in the audit log so you can always distinguish "Gabriel did X" from "the support agent did X".

Console endpoints are namespaced under `/v1/console/*` and never accept API keys. Agent endpoints are namespaced under `/v1/*` (no `console` prefix) and only accept API keys.
