> ## Documentation index
> Fetch the complete documentation index at: https://contextrepo.com/docs/llms.txt
> Use it to find every page before exploring further.

# Authentication

> Authenticate API requests using a Bearer JWT session token or an API key from your Context Repo dashboard.

Every API request requires an `Authorization` header. Context Repo supports two authentication methods: **Bearer JWT** for session-based access and **API-Key** for programmatic integrations.

## Bearer JWT

Use a session JWT token when making requests on behalf of a signed-in user. This is the same token your browser uses when you're logged into the dashboard.

**Header format:**

```
Authorization: Bearer <session_jwt_token>
```

The token must be a valid, non-expired session token issued by your signed-in dashboard session. Browser-based integrations built on top of Context Repo retrieve this token from their existing authenticated session — refer to your auth provider's documentation for the exact retrieval call.

Bearer tokens have **full access** to all resources owned by the authenticated user — they aren't restricted by permission scopes.

> **Note:** Bearer JWT authentication is primarily for browser-based, signed-in usage. For scripts, CLI tools, MCP clients, and server-to-server integrations, use an API key — it's simpler to set up and longer-lived.

## API Key

API keys provide scoped, long-lived access for external tools, scripts, and integrations like the [Chrome Extension](https://contextrepo.com/docs/integrations/chrome-extension) and [MCP clients](https://contextrepo.com/docs/mcp).

**Header format:**

```
Authorization: API-Key <your_api_key>
```

The header name `API-Key` is case-sensitive. API keys generated by the dashboard have the shape `gm_<publicId>_<secret>` — for example:

```
Authorization: API-Key gm_aB3cD4eF5gH6_xK9mN2pQ7rS5tU8vW1yZ3aB6cD9eF2gH5jK7
```

### Getting an API Key

1. Open the Context Repo dashboard
2. Go to **Settings > API Keys**
3. Click **New API Key**
4. Give it a name and select the permission scopes you need
5. Click **Create** and copy the key immediately — it's only shown once

For a detailed walkthrough of key creation, viewing, and revoking, see the [Settings](https://contextrepo.com/docs/dashboard/settings) page.

### Permission Scopes

API keys are scoped per resource and per access kind. Four scopes are available:

| Scope             | Label                           | What It Allows                                                                                                          |
| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `prompts.read`    | Prompts — Read                  | List, read, search, and inspect prompt versions                                                                         |
| `prompts.write`   | Prompts — Write                 | Create, update, delete, and restore prompt versions. **Implies `prompts.read`.**                                        |
| `documents.read`  | Documents & Collections — Read  | List, read, deep-search, and export documents and collections                                                           |
| `documents.write` | Documents & Collections — Write | Create, update, delete, restore versions, scrape URLs, and manage collection memberships. **Implies `documents.read`.** |

When you create a key in the dashboard, you pick **None / Read / Write** per resource — there is one selector for Prompts and another for Documents & Collections. Selecting "Write" automatically grants the matching read access, so you don't need to select both.

> **Warning:** If your API key doesn't have the required scope for an endpoint, the API returns a `403 Forbidden` error. Make sure you've assigned the right scopes for your use case.

### Scope Requirements by Endpoint

Read methods (GET) require the matching `.read` scope; write methods (POST/PATCH/DELETE/restore) require the matching `.write` scope (which already includes `.read`).

| Endpoint Group                                             | Read methods                                       | Write methods                                       |
| ---------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------- |
| Prompts (`/v1/prompts/*`)                                  | `prompts.read`                                     | `prompts.write`                                     |
| Documents (`/v1/documents/*`)                              | `documents.read`                                   | `documents.write`                                   |
| Document coordination (`/v1/documents/{id}/coordination*`) | `documents.read`                                   | `documents.write`; only the owner can change policy |
| Document scraping (`POST /v1/documents/scrape`)            | —                                                  | `documents.write`                                   |
| Collections (`/v1/collections/*`)                          | `documents.read`                                   | `documents.write`                                   |
| Search (`GET /v1/search`)                                  | At least one of `prompts.read` or `documents.read` | —                                                   |
| Progressive Disclosure (`/v1/pd/*`)                        | `documents.read`                                   | —                                                   |
| User profile (`GET /v1/user/me`)                           | Any authenticated request                          | —                                                   |
| MCP Capabilities (`GET /v1/mcp/capabilities`)              | None — public, unauthenticated                     | —                                                   |

## Sandbox & Public Endpoints

Context Repo exposes one **public, unauthenticated endpoint** so agents, scanners, and integration tools can verify connectivity, content-type negotiation, and CORS behavior before provisioning credentials:

| Endpoint                   | Auth | Purpose                                                                                                                |
| -------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/mcp/capabilities` | None | Returns the MCP protocol version, advertised capabilities, and supported tool surface. Static manifest, safe to cache. |

### Sandbox curl

```bash
curl -i https://api.contextrepo.com/v1/mcp/capabilities
```

Expect a `200 OK` with `Content-Type: application/json`, a 24-hour shared `Cache-Control: public, max-age=86400, s-maxage=86400` header, and an `X-API-Version: v1` header. The capabilities payload is a static manifest shared across every client, safe to cache aggressively at CDN and browser layers.

### Demo workflow without credentials

Agents and automated scanners that need to exercise the full request → response → error-recovery cycle without authentication can probe the public capabilities endpoint, then deliberately malform requests against other `/v1/*` routes to walk through the typed error envelopes:

```bash
# Capability probe — 200 OK with version + tool list
curl -i https://api.contextrepo.com/v1/mcp/capabilities

# Auth-required endpoint without credentials — 401 with ErrorEnvelope
curl -i https://api.contextrepo.com/v1/documents

# Malformed body on a write — 400 with field-level details
curl -i -X POST https://api.contextrepo.com/v1/documents \
  -H "Authorization: API-Key gm_demo_invalid" \
  -H "Content-Type: application/json" \
  -d 'not-json'
```

Every error response uses the same `ErrorEnvelope` shape (see [Error Format](https://contextrepo.com/docs/api#error-format)) — a single error path covers `400 / 401 / 403 / 404 / 409 / 413 / 429 / 500`. Agents can therefore wire one parser against the public capability probe and re-use it across the authenticated surface.

### Sandbox response headers

Every `/v1/*` 2xx response also carries machine-readable rate-limit and version signals so an agent can self-pace its budget and detect future deprecations without parsing release notes:

| Header                                      | Example                    | Purpose                                                                                                                        |
| ------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `RateLimit-Limit`                           | `100`                      | RFC 9598 — total requests allowed in the current window                                                                        |
| `RateLimit-Remaining`                       | `99`                       | RFC 9598 — requests left in the current window                                                                                 |
| `RateLimit-Reset`                           | `2026-05-20T13:00:00.000Z` | RFC 9598 — ISO-8601 timestamp when the window resets                                                                           |
| `X-RateLimit-Limit` / `Remaining` / `Reset` | (same values)              | Legacy alias — preserved for older agents that grep for `X-`-prefixed headers                                                  |
| `X-API-Version`                             | `v1`                       | Route-version constant; switches to `v2`, `v3`, … when new majors ship                                                         |
| `Deprecation`                               | `?0`                       | RFC 9745 structured field — `?0` means **not deprecated**; flips to `?1` and gains a `Sunset` header when a version is retired |

The publicly cacheable `/v1/mcp/capabilities` endpoint **omits** the `RateLimit-*` headers to keep its 24-hour CDN cache from baking one client's stale budget data into the response served to every other client. The `X-API-Version` and `Deprecation` headers still flow through because they are route-constant, not caller-scoped.

### Why no anonymous write tier

Mutation endpoints (`POST`, `PATCH`, `DELETE`) require authentication by design: writes touch user-scoped resources, are versioned per user, and bill against per-user rate-limit buckets. To exercise the full surface — including the async scrape + poll loop documented in [Asynchronous Operations](https://contextrepo.com/docs/api#asynchronous-operations) — create an API key via [Settings → API Keys](https://contextrepo.com/docs/dashboard/settings).

## Idempotency

Document create, update, restore, refresh, and scrape writes accept an optional
`Idempotency-Key` HTTP header in standard editing mode. Coordinated document
writes require it, and every lease operation requires it. Sending the same key
with the same request within a 24-hour window returns the original compact
response instead of re-executing the write.

```
Idempotency-Key: 8c3f1a92-7e4d-4f1b-9a01-2b7c5d6e8f10
```

| Endpoint                                     | Status                              |
| -------------------------------------------- | ----------------------------------- |
| `POST /v1/documents/scrape`                  | Supported                           |
| `POST /v1/documents`                         | Supported                           |
| `PATCH /v1/documents/{id}`                   | Supported; required in managed mode |
| `POST /v1/documents/{id}/restore`            | Supported; required in managed mode |
| `POST /v1/documents/{id}/refresh`            | Supported; required in managed mode |
| `POST /v1/documents/{id}/coordination/lease` | Required                            |

For a coordinated update, restore, or refresh, the header-based form sends
the key with the document precondition and current lease:

```text
If-Match: "<exact ETag from GET /v1/documents/{id}>"
Idempotency-Key: <unique retry key>
X-ContextRepo-Lease-Id: <lease ID>
X-ContextRepo-Lease-Fence: <fence number>
```

PATCH and restore accept either `If-Match` or body `expectedRevision`.
When both are supplied, their revisions must agree and the header's content
hash is also checked. Managed PATCH and restore additionally require
`Idempotency-Key`, `X-ContextRepo-Lease-Id`, and
`X-ContextRepo-Lease-Fence`. Refresh and coordination-policy operations
retain their existing preconditions.

Retry the identical operation with its original revision, key, payload, and
lease fields. Do not replace the revision or key to recover an uncertain
result. Matching receipts are retained for 24 hours from creation.
Reconcile uncertain operations by reading the document and history before
taking further action.

### Sample replay

```bash
# First call — runs Firecrawl, returns documentId "doc_a1b2"
curl -i -X POST https://api.contextrepo.com/v1/documents/scrape \
  -H "Authorization: API-Key gm_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8c3f1a92-7e4d-4f1b-9a01-2b7c5d6e8f10" \
  -d '{"url":"https://example.com"}'
# HTTP/1.1 202 Accepted
# Location: /v1/documents/doc_a1b2

# Replay with the SAME key and body — Firecrawl is NOT invoked
curl -i -X POST https://api.contextrepo.com/v1/documents/scrape \
  -H "Authorization: API-Key gm_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8c3f1a92-7e4d-4f1b-9a01-2b7c5d6e8f10" \
  -d '{"url":"https://example.com"}'
# HTTP/1.1 202 Accepted
# Location: /v1/documents/doc_a1b2
# Idempotent-Replayed: true
```

### Contract

| Behavior                                                | Status                          | Notes                                                 |
| ------------------------------------------------------- | ------------------------------- | ----------------------------------------------------- |
| Fresh key                                               | original status (`201` / `202`) | Handler runs once; response is cached for 24h         |
| Replay (same key + same body)                           | original status                 | `Idempotent-Replayed: true` advisory header set       |
| Same key + **different body**                           | `422 Unprocessable Entity`      | Reusing one key for two distinct payloads is rejected |
| Same key + first request **still processing**           | `409 Conflict`                  | Retry after the first finishes                        |
| Invalid header (>255 chars / control chars / non-ASCII) | `400 Bad Request`               |                                                       |

Keys are scoped per-user and per-endpoint. Two different users sending the same key value get independent fresh executions; the same user can reuse a key value across `POST /v1/documents` and `POST /v1/documents/scrape` without collision.

### TTL and failure caching

- Ledger rows expire 24 hours after the first request and are purged by a background cron.
- Successful responses (`2xx`) and deterministic client errors (`4xx`) are cached.
- Transient server errors (`5xx`) are **never** cached — the ledger row is deleted on failure so a subsequent retry with the same key is treated as fresh.

## Choosing an Auth Method

| Use Case                                   | Recommended Method                                                                                                                    |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Browser-based integrations                 | Bearer JWT                                                                                                                            |
| Chrome Extension                           | API Key with `documents.write` (needed to save scraped pages)                                                                         |
| MCP clients (Cursor, Claude Desktop, etc.) | API Key with the scopes matching the MCP tools you'll use — for full read + write access, grant `prompts.write` and `documents.write` |
| CI/CD pipelines and scripts                | API Key with the minimum scopes needed                                                                                                |
| Custom applications                        | API Key for server-to-server, Bearer JWT for user-facing                                                                              |

## Error Responses

Authentication failures return one of two status codes:

| Status             | Meaning                                                                           |
| ------------------ | --------------------------------------------------------------------------------- |
| `401 Unauthorized` | Missing or invalid `Authorization` header — the token or key couldn't be verified |
| `403 Forbidden`    | Valid authentication, but the API key doesn't have the required permission scope  |

```json
{
  "error": {
    "code": 401,
    "message": "Invalid authentication"
  }
}
```

## Related Pages

- [Settings](https://contextrepo.com/docs/dashboard/settings): Create, view, and revoke API keys from your dashboard.
- [Endpoints](https://contextrepo.com/docs/api/endpoints): Full REST endpoint reference with authentication requirements.
- [MCP Server](https://contextrepo.com/docs/mcp): Connect AI assistants using MCP — uses API key authentication.
