API Reference
Programmatic access to your prompts, documents, and collections through Context Repo's REST API.
Context Repo exposes a RESTful API for managing prompts, documents, collections, and search programmatically. Every endpoint requires authentication — see Authentication for setup details.
Base URL
All API requests are made to a single public endpoint:
https://api.contextrepo.comEvery path is prefixed with /v1/. For example, to list your prompts:
https://api.contextrepo.com/v1/promptsResponse Format
All successful responses return a JSON envelope with a data field:
{
"data": {
"id": "abc123",
"title": "My Prompt",
"description": "A helpful code review prompt"
}
}List endpoints include a pagination object alongside data:
{
"data": [
{ "id": "abc123", "title": "My Prompt" }
],
"pagination": {
"cursor": "eyIxIjoiMjAifQ==",
"hasMore": true
}
}Pass the cursor value as a query parameter on the next request to fetch the next page.
Error Format
All errors return a JSON envelope with a single error object. The shape is consistent across every endpoint and every status code:
{
"error": {
"code": 401,
"message": "Invalid authentication",
"details": null
}
}| Field | Type | Description |
|---|---|---|
error.code | integer | Mirrors the HTTP status code (400, 401, 403, 404, 409, 413, 429, 500). |
error.message | string | Human-readable description safe to surface to end users. |
error.details | any (nullable) | Optional structured context — e.g., the field that failed validation. Absent or null on most errors. |
Status Code Reference
| HTTP | Recoverable | Meaning | Typical Cause |
|---|---|---|---|
400 | After fix | Bad Request | Missing required field, malformed JSON, invalid query parameter, bad ID format |
401 | After fix | Unauthorized | Missing Authorization header, expired JWT, revoked API key |
403 | After fix | Forbidden | Valid credentials but the API key lacks the required scope |
404 | No | Not Found | Resource doesn't exist or the caller has no access |
409 | After fix | Conflict | Idempotent constraint violation (e.g., duplicate slug) |
413 | After fix | Payload Too Large | Request body exceeds the per-endpoint size cap |
429 | Yes — retry | Rate Limited | Quota exhausted — wait Retry-After seconds, then retry |
500 | Yes — retry | Internal Server Error | Transient failure — retry with exponential backoff |
Recovery Patterns
The full machine-readable schema lives in /openapi.json under components.schemas.ErrorEnvelope. The patterns below cover the recoverable codes:
if (res.status === 401) {
// Fetch a fresh Bearer JWT or rotate the API key; never retry the same key
throw new AuthError(await res.json());
}if (res.status === 429) {
const retryAfterSec = Number(res.headers.get("Retry-After") ?? 60);
await sleep(retryAfterSec * 1000);
return retry(req); // single retry — see Rate Limiting for the full backoff
}async function withBackoff(req: () => Promise<Response>, max = 4) {
for (let attempt = 0; attempt < max; attempt++) {
const res = await req();
if (res.status < 500) return res;
const base = 2 ** attempt * 500; // 500ms, 1s, 2s, 4s
const jitter = Math.random() * 250; // de-correlate concurrent retries
await sleep(base + jitter);
}
throw new Error("Upstream unavailable after retries");
}CORS Policy
The API allows requests from all origins. Every response includes these headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400Preflight OPTIONS requests are handled automatically for all /v1/ paths and return a 204 No Content response with CORS headers.
Rate Limiting
Every request is rate-limited per authenticated identity (per-user JWT or per-API-key). Buckets are sliding 60-second windows; exceeding a bucket returns 429 Rate Limited with a Retry-After hint.
Per-Tier Limits
| Tier | Endpoints | Limit | Window |
|---|---|---|---|
| Read | GET /v1/documents, GET /v1/collections, GET /v1/prompts/{id}, GET /v1/documents/{id}, GET /v1/collections/{id}, GET /v1/user/me, GET /v1/pd/read/*, POST /v1/pd/expand | 120 req | 60 s |
| Write | POST / PATCH / PUT / DELETE on all CRUD resources, GET /v1/prompts, GET /v1/search, POST /v1/pd/search | 100 req | 60 s |
| Scrape | POST /v1/documents/scrape | 10 req | 60 s |
Response Headers
The following headers are emitted on 429 responses. Clients should read Retry-After first, then fall back to deriving wait time from X-RateLimit-Reset if absent:
| Header | Type | Description |
|---|---|---|
X-RateLimit-Limit | integer | Total requests allowed in the current window |
X-RateLimit-Remaining | integer | Requests remaining before throttling kicks in |
X-RateLimit-Reset | ISO 8601 timestamp | When the window resets and the budget refills |
Retry-After | integer seconds | Wait this long before retrying (present on 429 only) |
Example 429 Response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-05-18T19:01:00.000Z
Retry-After: 38
{
"error": {
"code": 429,
"message": "Rate limit exceeded"
}
}Recommended Retry Strategy
- Honor
Retry-Afterliterally — never retry sooner than the server tells you. - Exponential backoff with jitter on transient failures (
5xx, network errors). Start at 500 ms, double per attempt, cap at 8 s, add ±250 ms jitter to de-correlate clients. See the Recovery Patterns snippet under Error Format. - Cap retry attempts at 4–5. Beyond that, surface the failure to the caller; the limiter is signalling a sustained backpressure condition, not a transient blip.
- Cap concurrency on the Scrape tier. With 10 req/60 s, more than one or two parallel scrapers per workspace will hit the limit immediately.
- Bucket your token budget. If you're a server-side integration making bursts, pre-emptively pause when
X-RateLimit-Remainingdrops below 5 to avoid the round-trip cost of a429.
API Versioning
The REST surface is versioned with a URL prefix (/v1/). The contract for a published version:
- Backwards compatibility within a major version. Inside
/v1/, fields and endpoints are additive only — new optional fields and new endpoints may appear at any time, but existing fields will not be renamed or repurposed, and existing endpoints will not have their semantics changed. - New majors get their own prefix. A future
/v2/will run side-by-side with/v1/rather than replace it. Clients pin to the prefix they were written against. - Deprecation runway. When a future major is introduced, the prior major remains available for at least six months. During the runway, responses from the deprecated major will include a
DeprecationHTTP header and aSunsetHTTP header naming the retirement date (per RFC 8594 and RFC 9745). - Breaking changes are never silent. If a
/v1/endpoint cannot be evolved additively, it ships as a new endpoint at a new path under/v1/or moves to/v2/— the old path keeps its prior contract.
The current major is v1. Detect the version client-side by inspecting the URL path or the servers block in /openapi.json.
Asynchronous Operations
Most endpoints respond synchronously with the final resource state. A small set of operations run in the background and respond with 202 Accepted plus a handle the client polls:
| Endpoint | Async Reason | Handle Field |
|---|---|---|
POST /v1/documents/scrape | URL extraction can take 10–60 s for large pages | data.documentId |
The Pattern
- Send the request. A
202 Acceptedresponse includes the resource identifier (documentId) before the work is finished. - Poll the GET endpoint for that resource (
GET /v1/documents/{id}) every 2–5 s. - The resource transitions through
statusvalues as work completes (pending→processing→completed). Thecontentfield is populated oncestatus === "completed". - Stop polling once
statusis terminal (completedorfailed). Surface failures using the sameErrorEnvelopeshape returned by synchronous endpoints.
const create = await fetch("https://api.contextrepo.com/v1/documents/scrape", {
method: "POST",
headers: { Authorization: `API-Key ${key}`, "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com/article" }),
});
if (create.status !== 202) throw new Error("scrape failed to enqueue");
const { data: { documentId } } = await create.json();
for (let i = 0; i < 30; i++) { // ~150 s ceiling at 5 s intervals
await sleep(5_000);
const poll = await fetch(`https://api.contextrepo.com/v1/documents/${documentId}`, {
headers: { Authorization: `API-Key ${key}` },
});
const { data } = await poll.json();
if (data.status === "completed") return data;
if (data.status === "failed") throw new Error(`scrape failed: ${data.status}`);
}
throw new Error("scrape timed out");The Scrape tier is rate-limited at 10 req/60 s — far stricter than the standard Write tier — because each call spawns background work. Cap parallel scrapes at 1–2 per workspace.
What's Next
Other MCP Clients
Connect Context Repo to any MCP-compatible client using stdio or Streamable HTTP transport. This guide covers the generic setup for clients not listed in our specific guides.
Authentication
Authenticate API requests using a Bearer JWT session token or an API key from your Context Repo dashboard.