{"openapi":"3.1.0","info":{"title":"ContextRepo REST API","version":"2.0.3","summary":"Public REST API for prompts, documents, collections, search, and progressive disclosure.","description":"ContextRepo is the AI Context Repo for agents. The REST API exposes 29 authenticated `/v1/*` operations for managing prompts, documents, collections, semantic and literal search, web scraping, and hierarchical chunk navigation. Authentication is via Clerk-issued bearer tokens (session JWT, OAuth `oat_*`, machine `mt_*`/`ak_*`) or first-party `gm_*` API keys. Requests must include a descriptive `User-Agent`.","termsOfService":"https://contextrepo.com/policies","contact":{"name":"Context Repo Support","url":"https://contextrepo.com","email":"support@contextrepo.com"},"license":{"name":"Proprietary - ContextRepo","url":"https://contextrepo.com/policies"},"x-detailedDescription":"Hosted on the Convex deployment fronted by `api.contextrepo.com`. MCP transport, Clerk webhooks, CORS preflight, and legacy `/v1/mcp/capabilities` are out of scope for this spec; see `/.well-known/mcp/server-card.json`. The `api.contextrepo.com` edge layer rejects requests with library-default User-Agent strings (e.g., raw `Python-urllib/3.x`) with `403` and body `error code: 1010` (plain text, not the JSON envelope). All `/v1/*` responses use HTTP/1.1 chunked transfer encoding; the MCP companion at `/mcp` additionally supports SSE-framed JSON-RPC."},"servers":[{"url":"https://api.contextrepo.com","description":"Production"}],"tags":[{"name":"prompts","description":"Prompt CRUD, versions, restore, and search"},{"name":"documents","description":"Document CRUD, versions, restore, and scrape"},{"name":"collections","description":"Collection CRUD and item membership"},{"name":"search","description":"Cross-resource semantic and literal search"},{"name":"progressive-disclosure","description":"Hierarchical chunk navigation for large documents"},{"name":"user","description":"Authenticated user identity and metadata"}],"externalDocs":{"description":"Full developer documentation and integration guides","url":"https://contextrepo.com/docs"},"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"paths":{"/v1/collections":{"get":{"tags":["collections"],"operationId":"listCollections","summary":"List collections owned by the authenticated user","description":"Returns a paginated list of collections the caller can read. Supports `limit`, `cursor` (base64-encoded offset), `search` (substring on name/description), `workspace` (workspaceId scope), and `tags` (AND-semantics filter; items must have all supplied tags). Requires `documents.read` for machine tokens and API keys; session JWTs bypass scope gating.","x-context-repo-required-scope":"documents.read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Search"},{"$ref":"#/components/parameters/Workspace"},{"$ref":"#/components/parameters/Tags"}],"responses":{"200":{"description":"Collections listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CollectionListItem"}},"pagination":{"$ref":"#/components/schemas/PaginationInfo"}},"required":["data","pagination"]},"example":{"data":[{"id":"k57a8b9c0d1e2f3g4h5i6j7k8l","name":"Marketing Prompts","description":"Reusable prompts for content drafts and ad copy","itemCount":12,"createdAt":1715731200000,"updatedAt":1715817600000}],"pagination":{"cursor":"","hasMore":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"post":{"tags":["collections"],"operationId":"createCollection","summary":"Create a new collection","description":"Creates a collection owned by the authenticated user. Returns 201 with a `Location` header pointing at `/v1/collections/{id}`. Requires `documents.write` for machine tokens and API keys.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionCreateRequest"}}}},"responses":{"201":{"description":"Collection created","headers":{"Location":{"schema":{"type":"string"},"description":"Relative URL of the created resource"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Collection"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/documents/scrape":{"post":{"tags":["documents"],"operationId":"scrapeUrl","summary":"Scrape a URL into a new document","description":"Submits a URL to be scraped, parsed, and stored as a new document. Returns 202 Accepted with the freshly-created `documentId` while chunking and embedding finish in the background. Requires `documents.write` for machine tokens and API keys (the legacy scope `documents.scrape` is also accepted for forward-compat). Bound by the `scrape` Redis rate-limit tier (much stricter than the `api` tier).","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScrapeRequest"}}}},"responses":{"202":{"description":"Scrape accepted - chunking and embedding will finish in the background","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"documentId":{"type":"string"}},"required":["documentId"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/documents":{"post":{"tags":["documents"],"operationId":"createDocument","summary":"Create a new document","description":"Creates a document with `title` + `content`. Optional `tags`, `collectionIds`, and `workspaceId`. Returns 201 with a `Location` header.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCreateRequest"}}}},"responses":{"201":{"description":"Document created","headers":{"Location":{"schema":{"type":"string"},"description":"Relative URL of the created document"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Document"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"get":{"tags":["documents"],"operationId":"listDocuments","summary":"List documents owned by the authenticated user","description":"Returns a paginated list of documents. Supports `limit`, `cursor`, `search`, `workspace`, `collectionId` (filter by collection membership), `status`, `includeArchived`, `includeContent` (when true, content is truncated to 2,000 characters per item), and `tags` (AND-semantics filter; items must have all supplied tags).","x-context-repo-required-scope":"documents.read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Search"},{"$ref":"#/components/parameters/Workspace"},{"name":"collectionId","in":"query","schema":{"type":"string"},"description":"Restrict to documents in this collection"},{"name":"status","in":"query","schema":{"type":"string","enum":["draft","published","archived"]}},{"name":"includeArchived","in":"query","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/IncludeContent"},{"$ref":"#/components/parameters/Tags"}],"responses":{"200":{"description":"Documents listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentListItem"}},"pagination":{"$ref":"#/components/schemas/PaginationInfo"}},"required":["data","pagination"]},"example":{"data":[{"id":"k1a2b3c4d5e6f7g8h9i0j1k2l3","title":"Onboarding Guide","status":"published","wordCount":1842,"createdAt":1715731200000,"updatedAt":1715900000000}],"pagination":{"cursor":"eyJvIjoyNX0=","hasMore":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/documents/{id}":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["documents"],"operationId":"getDocument","summary":"Get a document by id","description":"Returns the full document including `content`, `contentHtml`, and `rawHtml`. Each text field is capped at 2 MB on the wire (`REST_WIRE_FIELD_CAP_BYTES`); larger values are truncated. For binary blobs spilled to Convex storage, the response includes signed download URLs.","x-context-repo-required-scope":"documents.read","responses":{"200":{"description":"Document found","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Document"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"patch":{"tags":["documents"],"operationId":"updateDocument","summary":"Update a document","description":"Patches one or more of: `title`, `content`, `tags`, `status`. Omitted fields are left untouched. Creating a new content version is automatic.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentUpdateRequest"}}}},"responses":{"200":{"description":"Document updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Document"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"tags":["documents"],"operationId":"deleteDocument","summary":"Delete a document","description":"Permanently deletes a document and all of its versions, chunks, and embeddings. Returns 200 with `{success, id}` (NOT 204) - this 200+body envelope is required for MCP-client compatibility (Mission M-048).","x-context-repo-required-scope":"documents.write","responses":{"200":{"description":"Document deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAck"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/documents/{id}/versions":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["documents"],"operationId":"getDocumentVersions","summary":"List version history for a document","description":"Returns all stored versions of a document in newest-first order. Each version row includes `versionId`, `version`, `changeLog`, `createdAt`, `createdBy`, and a content preview.","x-context-repo-required-scope":"documents.read","responses":{"200":{"description":"Versions listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentVersion"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/documents/{id}/restore":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"post":{"tags":["documents"],"operationId":"restoreDocumentVersion","summary":"Restore a document to a previous version","description":"Creates a new version with content copied from the specified `versionId` and triggers re-chunking + re-embedding.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"versionId":{"type":"string"}},"required":["versionId"]}}}},"responses":{"200":{"description":"Document restored - new version created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Document"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/collections/{id}":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["collections"],"operationId":"getCollection","summary":"Get a collection by id","description":"Returns the collection metadata plus optional `stats` block (`itemCount`, `documentCount`, `promptCount`).","x-context-repo-required-scope":"documents.read","responses":{"200":{"description":"Collection found","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Collection"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"patch":{"tags":["collections"],"operationId":"updateCollection","summary":"Update a collection","description":"Patches one or more of: `name`, `description`, `color`, `icon`. Omitted fields are left untouched.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionUpdateRequest"}}}},"responses":{"200":{"description":"Collection updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Collection"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"tags":["collections"],"operationId":"deleteCollection","summary":"Delete a collection","description":"Permanently deletes the collection. The collection MUST be empty - any contained documents or prompts must be removed first. Returns 200 with `{success, id}` (Mission M-048). Returns 409 Conflict if the collection still has items.","x-context-repo-required-scope":"documents.write","responses":{"200":{"description":"Collection deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAck"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/collections/{id}/items":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["collections"],"operationId":"getCollectionItems","summary":"List the items in a collection","description":"Returns the items (documents and prompts) inside a collection. When `includeContent=true`, full content is returned in a single un-paginated response. When false, only metadata is returned.","x-context-repo-required-scope":"documents.read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/IncludeContent"}],"responses":{"200":{"description":"Items listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CollectionItem"}},"pagination":{"$ref":"#/components/schemas/PaginationInfo"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"post":{"tags":["collections"],"operationId":"addItemsToCollection","summary":"Add items to a collection","description":"Add one or more documents or prompts to a collection. Returns counts of newly-added items vs items that were already members.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionMembershipMutation"}}}},"responses":{"200":{"description":"Items added","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"added":{"type":"integer"},"alreadyPresent":{"type":"integer"}},"required":["added","alreadyPresent"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"put":{"tags":["collections"],"operationId":"removeItemsFromCollection","summary":"Remove items from a collection","description":"PUT (NOT DELETE-with-body) is used to remove items from a collection because DELETE-with-request-body is non-standard and rejected by some CDNs. Returns the count of removed items.","x-context-repo-required-scope":"documents.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionMembershipMutation"}}}},"responses":{"200":{"description":"Items removed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"removed":{"type":"integer"}},"required":["removed"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/search":{"get":{"tags":["search"],"operationId":"search","summary":"Search prompts, documents, and collections","description":"Cross-resource search with two modes:\n\n- `semantic=true` (default): vector similarity using OpenAI text-embedding-3-small (1536 dims).\n- `semantic=false`: literal substring match on titles, descriptions, and the first ~4 KiB of document content (the FTS cap).\n\nResults are grouped by resource type. Both branches honor `tags` (AND-semantics filter applied symmetrically to prompts, documents, and collections). Requires any read scope (`prompts.read`, `documents.read`).","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Free-form search query"},{"name":"semantic","in":"query","schema":{"type":"boolean","default":true}},{"name":"type","in":"query","schema":{"type":"string","enum":["prompts","documents","collections","all"],"default":"all"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Tags"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"},"example":{"data":{"prompts":[{"id":"k9z8y7x6w5v4u3t2s1r0q9p8o7","title":"Code review prompt","description":"Reviews a code diff for correctness, style, and security","engine":"gpt-4o","tags":["engineering","code-review"],"createdAt":1715731200000,"updatedAt":1715817600000}],"documents":[{"id":"k1a2b3c4d5e6f7g8h9i0j1k2l3","title":"Engineering Handbook","status":"published","wordCount":8421,"createdAt":1715731200000,"updatedAt":1715900000000}],"collections":[]},"meta":{"query":"code review","semantic":true,"latencyMs":142}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/user/me":{"get":{"tags":["user"],"operationId":"getMe","summary":"Get the authenticated identity","description":"Returns the resolved authentication identity, including `kind` (`session` | `machine` | `apiKey`), `userId`, profile fields, and the active `permissions` array. Useful for verifying which token / API key is in use and which scopes are granted.","responses":{"200":{"description":"Identity resolved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/User"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/prompts":{"get":{"tags":["prompts"],"operationId":"listPrompts","summary":"List prompts owned by the authenticated user","description":"Returns a paginated list of prompts. Supports `limit`, `cursor`, `search` (substring on title/description), and `tags` (AND-semantics filter; items must have all supplied tags).","x-context-repo-required-scope":"prompts.read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Search"},{"$ref":"#/components/parameters/Tags"}],"responses":{"200":{"description":"Prompts listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PromptListItem"}},"pagination":{"$ref":"#/components/schemas/PaginationInfo"}},"required":["data","pagination"]},"example":{"data":[{"id":"k9z8y7x6w5v4u3t2s1r0q9p8o7","title":"Code review prompt","description":"Reviews a code diff and surfaces correctness, style, and security issues","engine":"gpt-4o","tags":["engineering","code-review"],"createdAt":1715731200000,"updatedAt":1715817600000}],"pagination":{"cursor":"","hasMore":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"post":{"tags":["prompts"],"operationId":"createPrompt","summary":"Create a new prompt","description":"Creates a prompt with required `title`, `description`, `content`, and `engine`. Optional `tags`, `variables` (typed user-curated metadata: `{name, type: \"string\"|\"number\"|\"boolean\", description?}[]`), `parameters`, and `collectionIds`. Returns 201 + `Location` header.","x-context-repo-required-scope":"prompts.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCreateRequest"}}}},"responses":{"201":{"description":"Prompt created","headers":{"Location":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Prompt"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/prompts/{id}":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["prompts"],"operationId":"getPrompt","summary":"Get a prompt by id","description":"Returns the full prompt including `content`, `variables`, `parameters`, and version metadata.","x-context-repo-required-scope":"prompts.read","responses":{"200":{"description":"Prompt found","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Prompt"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"patch":{"tags":["prompts"],"operationId":"updatePrompt","summary":"Update a prompt","description":"Patches one or more of: `title`, `description`, `content`, `engine`, `tags`, `variables`, `parameters`. Omitted fields are left untouched.","x-context-repo-required-scope":"prompts.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptUpdateRequest"}}}},"responses":{"200":{"description":"Prompt updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Prompt"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"tags":["prompts"],"operationId":"deletePrompt","summary":"Delete a prompt","description":"Permanently deletes a prompt and all of its versions. Returns 200 with `{success, id}` (Mission M-048).","x-context-repo-required-scope":"prompts.write","responses":{"200":{"description":"Prompt deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAck"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/prompts/{id}/versions":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"tags":["prompts"],"operationId":"getPromptVersions","summary":"List version history for a prompt","description":"Returns all stored versions of a prompt in newest-first order. Each row includes `versionId`, `version`, `changeLog`, `createdAt`, `createdBy`, and a content preview.","x-context-repo-required-scope":"prompts.read","responses":{"200":{"description":"Versions listed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PromptVersion"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/prompts/{id}/restore":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"post":{"tags":["prompts"],"operationId":"restorePromptVersion","summary":"Restore a prompt to a previous version","description":"Creates a new version with content copied from the specified `versionId`.","x-context-repo-required-scope":"prompts.write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"versionId":{"type":"string"}},"required":["versionId"]}}}},"responses":{"200":{"description":"Prompt restored","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Prompt"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/pd/search":{"post":{"tags":["progressive-disclosure"],"operationId":"pdSearch","summary":"Vector search returning ranked, hierarchical document chunks","description":"Progressive-disclosure search: returns granular content fragments (chunks) with structural metadata (chunkId, level, parentId, sectionPath, etc.) so an agent can navigate up/down the document tree without loading the entire body.","x-context-repo-required-scope":"documents.read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdSearchRequest"}}}},"responses":{"200":{"description":"Chunk results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PdChunk"}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/pd/expand":{"post":{"tags":["progressive-disclosure"],"operationId":"pdExpand","summary":"Navigate hierarchically from a chunk","description":"Walks the document tree starting from `chunkId` in one of five directions: `up` (parent), `down` (children), `next`/`previous` (siblings), `surrounding` (context window of N siblings on each side; falls back to neighbouring sections if the target is the only child).","x-context-repo-required-scope":"documents.read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdExpandRequest"}}}},"responses":{"200":{"description":"Expansion results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PdChunk"}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/pd/read/{chunkId}":{"parameters":[{"name":"chunkId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["progressive-disclosure"],"operationId":"pdRead","summary":"Read a single chunk with full content + hierarchy metadata","description":"Returns one chunk's full text, sectionPath, chunkIndex, navigation IDs (parentChunkId, prevSiblingId, nextSiblingId), and content metadata (wordCount, headingText).","x-context-repo-required-scope":"documents.read","responses":{"200":{"description":"Chunk found","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PdChunk"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/v1/pd/session":{"post":{"tags":["progressive-disclosure"],"operationId":"pdCreateSession","summary":"Create a progressive-disclosure session for de-duplication","description":"Creates a session token used by `pdSearch` to de-duplicate chunks across iterative queries. Optional `ttl` (seconds, default 1800).","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ttl":{"type":"integer","minimum":60,"maximum":86400}}}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"sessionId":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["sessionId","expiresAt"]}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Clerk-issued bearer token. Three accepted forms:\n  1) Session JWT - issued to a logged-in dashboard or extension user.\n  2) `oat_*` - Clerk OAuth user-delegation token (used by the ChatGPT MCP connector and other OAuth clients).\n  3) `mt_*` / `ak_*` - Clerk machine-to-machine token / Clerk-managed API key.\n\nForms (1) and (2) bypass scope gating; form (3) MUST carry the requested scope in the token's `scopes` claim. The OAuth metadata for token acquisition is published at `/.well-known/oauth-authorization-server` (delegated to Clerk) and `/.well-known/oauth-protected-resource/v1` (this REST resource)."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"ContextRepo first-party API key. Header value MUST be exactly `API-Key gm_<publicId>_<secret>` (note the leading `API-Key ` prefix and the underscore-delimited two-part secret). Permissions are taken from the key's `permissions` array stored in the `apiKeys` table; bcrypt is used to verify the secret."}},"parameters":{"Limit":{"name":"limit","in":"query","schema":{"type":"integer","default":20,"minimum":1,"maximum":100},"description":"Maximum number of items to return."},"Cursor":{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque pagination cursor returned by the previous list response. Some endpoints encode a base64 offset; others return a Convex paginated cursor. Treat as opaque."},"Search":{"name":"search","in":"query","schema":{"type":"string"},"description":"Substring filter on the resource's name/title/description."},"Workspace":{"name":"workspace","in":"query","schema":{"type":"string"},"description":"Restrict to resources in this workspaceId."},"IncludeContent":{"name":"includeContent","in":"query","schema":{"type":"boolean","default":false},"description":"When true, include resource content in the response (truncated to 2,000 characters per item on list endpoints)."},"IdPath":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Convex ID of the resource (a 27-character alphanumeric string)."},"Tags":{"name":"tags","in":"query","schema":{"type":"string"},"description":"Comma-separated list of tags. Items must match ALL supplied tags (AND semantics). Whitespace is trimmed; empty entries are ignored. Tag values must not contain commas."}},"responses":{"BadRequest":{"description":"Bad Request - malformed body or invalid parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error400"}}}},"Unauthorized":{"description":"Unauthorized - missing or invalid Authorization header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error401"}}}},"Forbidden":{"description":"Forbidden - the caller's identity does not include the required scope or does not own the resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error403"}}}},"NotFound":{"description":"Not Found - resource does not exist or is not visible to the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error404"}}}},"Conflict":{"description":"Conflict - the request conflicts with the current state of the resource (e.g., deleting a non-empty collection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error409"}}}},"PayloadTooLarge":{"description":"Payload Too Large - one of the body fields exceeds its byte cap (e.g., document content > 5 MB)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error413"}}}},"TooManyRequests":{"description":"Rate limit exceeded. Honor the `Retry-After` header.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Limit for the bucket"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining quota in the bucket"},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"ISO-8601 timestamp when the bucket refills"},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the next retry is allowed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error429"}}}},"InternalServerError":{"description":"Internal Server Error - unhandled exception in a route handler","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}},"schemas":{"Prompt":{"type":"object","description":"A prompt template owned by the authenticated user.","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"string"},"engine":{"type":"string","description":"Target AI engine identifier (e.g., gpt-4, claude-3, gemini-pro)"},"tags":{"type":"array","items":{"type":"string"}},"variables":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["string","number","boolean"]},"description":{"type":"string"}},"required":["name","type"]}},"parameters":{"type":"object","additionalProperties":true},"currentVersion":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","content","engine"]},"PromptListItem":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"engine":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title"]},"PromptVersion":{"type":"object","properties":{"versionId":{"type":"string"},"version":{"type":"integer"},"changeLog":{"type":"string"},"preview":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}},"required":["versionId","version","createdAt"]},"PromptCreateRequest":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"string"},"engine":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"variables":{"type":"array","items":{"$ref":"#/components/schemas/Prompt/properties/variables/items"}},"parameters":{"type":"object","additionalProperties":true},"collectionIds":{"type":"array","items":{"type":"string"}}},"required":["title","description","content","engine"]},"PromptUpdateRequest":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"string"},"engine":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"variables":{"type":"array","items":{"$ref":"#/components/schemas/Prompt/properties/variables/items"}},"parameters":{"type":"object","additionalProperties":true},"changeLog":{"type":"string"}}},"Document":{"type":"object","description":"A document with full content. Each text field is capped at 2 MB on the wire.","properties":{"id":{"type":"string"},"title":{"type":"string"},"content":{"type":"string"},"contentHtml":{"type":"string"},"rawHtml":{"type":"string"},"status":{"type":"string","enum":["draft","published","archived"]},"isArchived":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"sourceType":{"type":"string"},"sourceUrl":{"type":"string","format":"uri"},"currentVersion":{"type":"integer"},"stats":{"type":"object","additionalProperties":true},"workspaceId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title"]},"DocumentListItem":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","published","archived"]},"isArchived":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"updatedAt":{"type":"string","format":"date-time"},"workspaceId":{"type":"string"},"content":{"type":"string","description":"Truncated to 2,000 chars when includeContent=true"}},"required":["id","title"]},"DocumentVersion":{"type":"object","properties":{"versionId":{"type":"string"},"version":{"type":"integer"},"changeLog":{"type":"string"},"preview":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"}},"required":["versionId","version","createdAt"]},"DocumentCreateRequest":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"collectionIds":{"type":"array","items":{"type":"string"}},"workspaceId":{"type":"string"}},"required":["title","content"]},"DocumentUpdateRequest":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["draft","published","archived"]},"changeLog":{"type":"string"}}},"ScrapeRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"collectionIds":{"type":"array","items":{"type":"string"}},"options":{"type":"object","additionalProperties":true,"description":"Optional scraper options forwarded to the Firecrawl provider"}},"required":["url"]},"Collection":{"type":"object","description":"A collection (folder) of documents and prompts.","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string","description":"Single-emoji icon"},"isPublic":{"type":"boolean"},"parentCollectionId":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"itemCount":{"type":"integer"},"lastActivityAt":{"type":"string","format":"date-time"},"workspaceId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name"]},"CollectionListItem":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"itemCount":{"type":"integer"},"lastActivityAt":{"type":"string","format":"date-time"}},"required":["id","name"]},"CollectionCreateRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"parentCollectionId":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"workspaceId":{"type":"string"}},"required":["name"]},"CollectionUpdateRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}},"CollectionMembershipMutation":{"type":"object","properties":{"itemIds":{"type":"array","items":{"type":"string"}},"itemType":{"type":"string","enum":["document","prompt"]}},"required":["itemIds","itemType"]},"CollectionItem":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["document","prompt"]},"title":{"type":"string"},"preview":{"type":"string"},"addedAt":{"type":"string","format":"date-time"}},"required":["id","type","title"]},"User":{"type":"object","description":"Resolved authenticated identity returned by GET /v1/user/me.","properties":{"kind":{"type":"string","enum":["session","machine","apiKey"]},"userId":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"imageUrl":{"type":"string","format":"uri"},"permissions":{"type":"array","items":{"type":"string"}}},"required":["kind","userId","permissions"]},"SearchResults":{"type":"object","properties":{"data":{"type":"object","properties":{"prompts":{"type":"array","items":{"$ref":"#/components/schemas/PromptListItem"}},"documents":{"type":"array","items":{"$ref":"#/components/schemas/DocumentListItem"}},"collections":{"type":"array","items":{"$ref":"#/components/schemas/CollectionListItem"}}}},"meta":{"type":"object","properties":{"query":{"type":"string"},"semantic":{"type":"boolean"},"latencyMs":{"type":"integer"}}}},"required":["data"]},"PdSearchRequest":{"type":"object","properties":{"query":{"type":"string"},"documentId":{"type":"string","description":"Restrict to chunks of a single document"},"collectionId":{"type":"string","description":"Restrict to chunks of documents in a collection"},"sessionId":{"type":"string","description":"Optional session ID for de-duplication across iterative queries"},"limit":{"type":"integer","default":10,"minimum":1,"maximum":100}},"required":["query"]},"PdExpandRequest":{"type":"object","properties":{"chunkId":{"type":"string"},"direction":{"type":"string","enum":["up","down","next","previous","surrounding"]},"count":{"type":"integer","minimum":1,"maximum":25,"default":2,"description":"Neighbours per side (only used by direction=surrounding)"}},"required":["chunkId","direction"]},"PdChunk":{"type":"object","description":"A document chunk with hierarchy metadata.","properties":{"chunkId":{"type":"string"},"documentId":{"type":"string"},"level":{"type":"string","description":"Hierarchy level (document, section, paragraph)"},"headingText":{"type":"string"},"sectionPath":{"type":"array","items":{"type":"string"}},"chunkIndex":{"type":"integer"},"wordCount":{"type":"integer"},"content":{"type":"string"},"parentChunkId":{"type":"string"},"prevSiblingId":{"type":"string"},"nextSiblingId":{"type":"string"},"score":{"type":"number","format":"float","description":"Similarity score (0-1) when returned by pdSearch"}},"required":["chunkId","documentId","content"]},"PaginationInfo":{"type":"object","properties":{"cursor":{"type":"string","description":"Opaque cursor for the next page (empty string when no more results)"},"hasMore":{"type":"boolean"}},"required":["cursor","hasMore"]},"DeleteAck":{"type":"object","description":"Mission M-048 delete acknowledgment - 200 + body, NOT 204.","properties":{"success":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["success","id"]},"ErrorEnvelope":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"},"details":{}},"required":["code","message"]}},"required":["error"]},"Error400":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error401":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error403":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error404":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error409":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error413":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error429":{"$ref":"#/components/schemas/ErrorEnvelope"},"Error500":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}