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.
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 and MCP clients.
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_xK9mN2pQ7rS5tU8vW1yZ3aB6cD9eF2gH5jK7Getting an API Key
- Open the Context Repo dashboard
- Go to Settings > API Keys
- Click New API Key
- Give it a name and select the permission scopes you need
- 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 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.
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
curl -i https://api.contextrepo.com/v1/mcp/capabilitiesExpect 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:
# 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) — 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 — create an API key via Settings → API Keys.
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:
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
# 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: trueContract
| 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 |
{
"error": {
"code": 401,
"message": "Invalid authentication"
}
}