API Reference
Resource Model
The API operates on three core resource types:
| Resource | Path prefix | Description |
|---|---|---|
| Project | /v1/projects | Container for items and files |
| Item | /v1/projects/{project_key}/items | Inline text or file upload |
| Search | /v1/projects/{project_key}/search | Full-text search across items |
Items are the canonical resource type. Each item has a content_type indicating whether it stores inline text (inline) or an uploaded file (file). Both types share the same endpoint surface.
Authentication
All API requests require a Bearer token in the Authorization header. API keys are prefixed ctxme_ and validated against stored prefixes/hashes.
curl -H "Authorization: Bearer $API_KEY" https://api.ctx.me/v1/projects
Endpoints
Health
| Method | Path | Description |
|---|---|---|
| GET | /health | Service health check |
Auth
| Method | Path | Description |
|---|---|---|
| GET | /v1/auth/me | Current user + account context |
Projects
| Method | Path | Description |
|---|---|---|
| GET | /v1/projects | List projects for current account |
| GET | /v1/projects/{project_key} | Get a single project |
| POST | /v1/projects | Create a new project |
| PUT | /v1/projects/{project_key} | Update a project (ETag required) |
| DELETE | /v1/projects/{project_key} | Delete a project (ETag required) |
List Projects
Returns a paginated list of projects for the current account.
{
"projects": [ ... ],
"next_cursor": "eyJ...",
"has_more": true
}
Query parameters:
limit: Max projects per page (1-200, default 50)cursor: Opaque cursor from previous response'snext_cursor
Create Project
Returns 201 Created with Location and ETag headers.
HTTP/1.1 201 Created
Location: /v1/projects/my-project
ETag: "v1:2026-02-07T..."
Get Project
Supports conditional GET via If-None-Match header. Returns 304 Not Modified when the ETag matches.
Update Project
Full replacement of project metadata. Requires If-Match header.
Delete Project
Soft-deletes a project. Requires If-Match header. Returns 409 Conflict if the project still contains items.
Items
The unified items endpoint handles both inline text content and file uploads through a single API surface.
| Method | Path | Description |
|---|---|---|
| GET | /v1/projects/{project_key}/items | List items in a project |
| GET | /v1/projects/{project_key}/items/{item_key} | Get a single item |
| POST | /v1/projects/{project_key}/items | Create item (JSON or multipart) |
| PUT | /v1/projects/{project_key}/items/{item_key} | Replace an inline item (full update) |
| PATCH | /v1/projects/{project_key}/items/{item_key} | Patch item metadata (partial update) |
| DELETE | /v1/projects/{project_key}/items/{item_key} | Delete an item |
Creating Items
Items can be created in two ways:
Inline Content (JSON):
curl -X POST -H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"key": "my-item", "title": "My Item", "data": "content here"}' \
https://api.ctx.me/v1/projects/myproject/items
File Upload (multipart/form-data):
curl -X POST -H "Authorization: Bearer $API_KEY" \
-F "file=@document.docx" \
-F "key=my-document" \
https://api.ctx.me/v1/projects/myproject/items
For file uploads, the key is optional — if omitted, it's derived from the filename.
Supported upload formats are .txt, .md/.markdown, .pdf, .docx, .csv,
.tsv, .html, and .htm. File uploads return 202 Accepted while asynchronous
extraction runs.
PUT — Replace Inline Item
Full replacement of an inline item's content and metadata. Requires If-Match header. File items cannot be replaced via PUT — use PATCH to update metadata, or delete and re-upload.
PATCH — Partial Metadata Update
Partially updates item metadata. Requires If-Match header. Accepted fields (at least one required):
title(optional): New title (1-255 characters)mime_type(optional): New MIME type (not changeable for file items)tags(optional): New tags array
Content Types
Items have a content_type field indicating the storage type:
inline: Text content stored directly in the databasefile: Binary content stored in object storage
Processing Status
Items have a processing_status field:
ready: Content is fully indexed and searchableprocessing: Async extraction in progress (file uploads)failed: Processing failed (checkprocessing_errorfield)
List Items Query Parameters
content_type: Filter by type (inlineorfile)content_sha256: Filter by content hash for deduplication lookupcreated_via: Filter by provenance (ui,cli,mcp,unknown)limit: Max items per page (1-200, default 50)cursor: Opaque cursor from previous response'snext_cursor
Search
| Method | Path | Description |
|---|---|---|
| GET | /v1/search?q=<query> | Search context items |
Query parameters:
q(required): Search query (2-256 chars)project_key: Scope search to a single project. Omit to search all projects.tags: Comma-separated tags (AND semantics)include_pending: Include items with non-ready processing status (default false)created_via: Filter by provenance (ui,cli,mcp,unknown)limit: Results per page (1-50, default 20)cursor: Opaque cursor from previous response'snext_cursor
Pagination
List endpoints use cursor-based pagination for stable, efficient paging through results.
- Make the initial request (optionally with
limit). - The response includes
has_more(boolean) andnext_cursor(string or null). - If
has_moreis true, passnext_cursoras thecursorquery parameter in the next request. - Repeat until
has_moreis false.
# First page
curl -H "Authorization: Bearer $API_KEY" \
"https://api.ctx.me/v1/projects/myproject/items?limit=10"
# Next page (using next_cursor from previous response)
curl -H "Authorization: Bearer $API_KEY" \
"https://api.ctx.me/v1/projects/myproject/items?limit=10&cursor=eyJ..."
Conditional Requests and ETags
Optimistic Concurrency (If-Match)
Write operations that modify existing resources require the If-Match header to prevent lost updates. The value must match the resource's current etag.
# 1. Fetch the resource and capture its ETag
curl -i -H "Authorization: Bearer $API_KEY" \
https://api.ctx.me/v1/projects/myproject/items/my-item
# 2. Update with If-Match
curl -X PUT -H "Authorization: Bearer $API_KEY" \
-H 'If-Match: "v1:2026-02-07T12:00:00Z"' \
-H "Content-Type: application/json" \
-d '{"title": "Updated", "data": "new content", "mime_type": "text/plain", "tags": []}' \
https://api.ctx.me/v1/projects/myproject/items/my-item
Conditional GET (If-None-Match)
Read operations support the If-None-Match header to avoid re-downloading unchanged resources. Returns 304 Not Modified when the ETag matches.
Errors
Error responses follow a consistent envelope format:
{
"error": {
"code": "string",
"message": "Human-readable detail",
"details": {}
}
}
Common error codes:
| Status | Code | Description |
|---|---|---|
400 | validation_error | Invalid request (e.g., PUT on file items) |
401 | unauthorized | Invalid or missing API key |
403 | forbidden | Insufficient permissions |
404 | not_found | Resource not found |
409 | conflict | Duplicate key or content |
412 | precondition_failed | ETag mismatch (If-Match failed) |
422 | validation_error | Request validation failed |
428 | precondition_required | Missing required If-Match header |
429 | too_many_requests | Rate limited |
Versioning
All endpoints are prefixed with /v1/. The current API version is v1. Breaking changes within /v1/ are avoided where possible. Deprecated endpoints remain functional and will be announced before removal.