Context RepoContext Repo Docs
API Reference

API Reference

Programmatic access to stored context, retrieval, and reasoning through Context Repo's REST API.

Context Repo exposes a RESTful API for managing prompts, documents, collections, retrieval, and cited reasoning 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.com

Every path is prefixed with /v1/. For example, to list your prompts:

https://api.contextrepo.com/v1/prompts

Response 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
  }
}
FieldTypeDescription
error.codeintegerMirrors the HTTP status code (400, 401, 403, 404, 409, 412, 413, 422, 428, 429, 500).
error.reasonstring (optional)Stable machine-readable reason on document protocol errors, such as REVISION_MISMATCH.
error.messagestringHuman-readable description safe to surface to end users.
error.detailsany (nullable)Optional structured context — e.g., the field that failed validation. Absent or null on most errors.

Status Code Reference

HTTPRecoverableMeaningTypical Cause
400After fixBad RequestMissing required field, malformed JSON, invalid query parameter, bad ID format
401After fixUnauthorizedMissing Authorization header, expired JWT, revoked API key
403After fixForbiddenValid credentials but the API key lacks the required scope
404NoNot FoundResource doesn't exist or the caller has no access
409After reloadConflictLease conflict, stale fence, or operation still in progress
412After reloadPrecondition FailedThe document revision changed after you read it
413After fixPayload Too LargeRequest body exceeds the per-endpoint size cap
422After fixUnprocessable EntityAn idempotency key was reused for different inputs, or the document cannot enter coordination from its current snapshot
428After fixPrecondition RequiredA managed write omitted a required ETag, key, or lease header
429Yes — retryRate LimitedQuota exhausted — wait Retry-After seconds, then retry
500Yes — retryInternal Server ErrorTransient 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:

Handle 401 (refresh credentials, do not retry)
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());
}
Handle 429 (honor Retry-After)
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
}
Handle 5xx (exponential backoff with jitter)
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, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, If-Match, Idempotency-Key, X-ContextRepo-Lease-Id, X-ContextRepo-Lease-Fence
Access-Control-Expose-Headers: ETag, X-ContextRepo-Revision, X-ContextRepo-Content-Hash, X-ContextRepo-Lease-Expires-At, Idempotent-Replayed, Retry-After
Access-Control-Max-Age: 86400

Preflight 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

TierEndpointsLimitWindow
ReadGET /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/expand120 req60 s
APIPOST / PATCH / PUT / DELETE on all CRUD resources, GET /v1/prompts, GET /v1/search, POST /v1/pd/search, POST /v1/reason100 req60 s
ScrapePOST /v1/documents/scrape10 req60 s

Response Headers

Rate-limit metadata can appear on non-cacheable responses. On 429, clients should read Retry-After first, then fall back to deriving wait time from X-RateLimit-Reset if absent:

HeaderTypeDescription
X-RateLimit-LimitintegerTotal requests allowed in the current window
X-RateLimit-RemainingintegerRequests remaining before throttling kicks in
X-RateLimit-ResetISO 8601 timestampWhen the window resets and the budget refills
Retry-Afterinteger secondsWait 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"
  }
}
  1. Honor Retry-After literally — never retry sooner than the server tells you.
  2. 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.
  3. 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.
  4. 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.
  5. Bucket your token budget. If you're a server-side integration making bursts, pre-emptively pause when X-RateLimit-Remaining drops below 5 to avoid the round-trip cost of a 429.

API Versioning

The REST surface is versioned with a URL prefix (/v1/). Inside a major version, changes are additive only; new majors run side-by-side with the prior one, with a six-month deprecation runway signaled by Deprecation and Sunset HTTP headers. The current major is v1 — no version is deprecated today.

The full contract lives at Versioning & Deprecation Policy.

Document Revisions and Coordination

Completed documents expose a canonical revision in their body and response headers. For PATCH and restore, send body expectedRevision or forward the exact ETag from GET /v1/documents/{id} as If-Match for compare-and-swap protection. Changed writes advance revision; exact unchanged writes return the same compact acknowledgement with noop: true.

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.

Treat 409 and 412 as a signal to reload state rather than overwriting it. See the document endpoints for the full flow.

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:

EndpointAsync ReasonHandle Field
POST /v1/documents/scrapeURL extraction can take 10–60 s for large pagesdata.documentId

The Pattern

  1. Send the request. A 202 Accepted response includes the resource identifier (documentId) before the work is finished.
  2. Poll the GET endpoint for that resource (GET /v1/documents/{id}) every 2–5 s.
  3. The resource transitions through status values as work completes (pendingprocessingcompleted). The content field is populated once status === "completed".
  4. Stop polling once status is terminal (completed or failed). Surface failures using the same ErrorEnvelope shape returned by synchronous endpoints.
Poll pattern (TypeScript)
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