Context RepoContext Repo Docs
API Reference

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.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, 413, 429, 500).
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 fixConflictIdempotent constraint violation (e.g., duplicate slug)
413After fixPayload Too LargeRequest body exceeds the per-endpoint size cap
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, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
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
WritePOST / PATCH / PUT / DELETE on all CRUD resources, GET /v1/prompts, GET /v1/search, POST /v1/pd/search100 req60 s
ScrapePOST /v1/documents/scrape10 req60 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:

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/). 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 Deprecation HTTP header and a Sunset HTTP 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:

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