REST API
Direct HTTP examples for the CortexDB v1 surface.
CortexDB exposes a v1 REST API. Every endpoint accepts and returns JSON unless noted (/v1/blobs is
binary; SSE endpoints are text/event-stream). A self-hosted server
serves it at http://localhost:3141 (the recommended setup); the managed cloud is at
https://api-v1.cortexdb.ai.
For full endpoint shapes and field-by-field reference, browse the API Reference section.
Self-hosted auth: a key is required unless you opt out
A self-hosted server started with CORTEX_INSECURE_NO_AUTH=1 serves every request as user:local
with no headers — so the examples below reduce to the URL and JSON body. Otherwise pass
Authorization: Bearer <your CORTEX_API_KEY> (+ X-Cortex-Actor) as shown. A keyless, network-exposed
server auto-generates a key and requires it. See the Self-Hosting Quickstart.
30-second cold start
The shortest copy-paste path from "I have nothing" to "I stored and recalled a memory" — anonymous signup, no email or card:
# 1. Mint a free-tier token (returns token + user_id + scope + 7-day expiry)
SIGNUP=$(curl -sfS -X POST https://api-v1.cortexdb.ai/v1/auth/signup \
-H 'Content-Type: application/json' -d '{}')
export CORTEX_TOKEN=$(echo "$SIGNUP" | jq -r .token)
export CORTEX_ACTOR=$(echo "$SIGNUP" | jq -r .user_id)
export CORTEX_SCOPE=$(echo "$SIGNUP" | jq -r .scope)
# 2. Store a memory
curl -sfS -X POST https://api-v1.cortexdb.ai/v1/experience \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: $CORTEX_ACTOR" \
-H 'Content-Type: application/json' \
-d "{
\"scope\": \"$CORTEX_SCOPE\",
\"modality\": \"conversation\",
\"content\": { \"kind\": \"message\", \"role\": \"user\",
\"text\": \"Q3 revenue exceeded \$2.4M, up 34% YoY\" },
\"context\": { \"observed_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" },
\"idempotency_key\": \"cold-start-001\"
}"
# 3. Ask a question (uses the LLM-backed answer endpoint — citations included)
curl -sfS -X POST https://api-v1.cortexdb.ai/v1/answer \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: $CORTEX_ACTOR" \
-H 'Content-Type: application/json' \
-d "{ \"scope\": \"$CORTEX_SCOPE\", \"question\": \"What was Q3 revenue?\",
\"view\": \"holistic\", \"diagnostics\": \"none\" }"Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Reset,
X-Cortex-Token-Expires-In, and X-Cortex-Token-Expires-At — surface them in your client to back off
and refresh ahead of expiry. The free-tier token TTL is 7 days; re-sign-up to mint another, or use
POST /v1/auth/tokens once you have an account with the auth.mint capability.
Authentication
Every authenticated request requires a PASETO v4 public token plus the X-Cortex-Actor header:
curl https://api-v1.cortexdb.ai/v1/auth/whoami \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice"X-Cortex-Actor must match the token's sub claim — otherwise the server returns 401 actor_mismatch.
Self-hosted servers: if started with CORTEX_API_KEY=<value>, that value itself is a working bearer
(Authorization: Bearer <value>). To run unauthenticated for local dev, start with
CORTEX_INSECURE_NO_AUTH=1 — every request is served as user:local and no headers are needed. With
neither set on a network-exposed bind, the server auto-generates a key and requires it (printed in the
boot log).
Three ways to get a token (full decision table at Authorization):
| You want to… | Endpoint | Notes |
|---|---|---|
| Try the API in 60 seconds | POST /v1/auth/signup | Anonymous, free-tier, 7-day TTL. No email or card. |
| Run a service account | POST /v1/auth/tokens | Requires auth.mint capability. 1–24h TTL. |
| Use your own IdP in production | Register your signing key with CortexDB | Your IdP mints PASETO; CortexDB verifies. |
Base URL
| Environment | URL |
|---|---|
| Self-hosted (recommended) | http://localhost:3141 |
| Cloud | https://api-v1.cortexdb.ai |
Capture an experience
curl -X POST https://api-v1.cortexdb.ai/v1/experience \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/dept:eng/user:alice",
"modality": "conversation",
"content": {
"kind": "message",
"role": "user",
"text": "Just got off a call with Priya at Acme."
},
"context": { "observed_at": "2026-05-15T10:42:00Z" },
"idempotency_key": "alice-chat-001"
}'Required fields and wait semantics
Only scope, modality, content are required; context, observed_at, and
idempotency_key are optional. With ?wait= omitted the write returns 202 captured with
an event_id and a lifecycle_stream URL — the event is in the WAL but not yet indexed. Pass
?wait=indexed (or captured / consolidated) to block until that stage completes (returns 200).
?wait=accepted is an explicit alias for the omitted default (server ≥ v0.8.7); any other value
422s listing the valid values.
Recall a stratified pack
curl -X POST https://api-v1.cortexdb.ai/v1/recall \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/dept:eng/user:alice",
"view": "holistic",
"query": "What did we decide about the Q3 launch?",
"include": ["beliefs", "facts", "episodes"],
"budgets": { "max_tokens": 4000 },
"citation_mode": "inline_with_markers"
}'Recall + LLM answer
curl -X POST https://api-v1.cortexdb.ai/v1/answer \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/dept:eng/user:alice",
"view": "holistic",
"question": "Did Acme renew?",
"answer_model": "claude-opus-4-6",
"temporal": { "natural": "last 30 days" }
}'The answer field is question; the model is a cloud default
The answer endpoint's field is question (not query). claude-opus-4-6 is the cloud default
model; self-hosted, /v1/answer routes through the configured answer lane (CORTEX_ANSWER_*) and is
disabled until it's set — see Self-hosting defaults.
Layer reads
# Events
curl "https://api-v1.cortexdb.ai/v1/events?scope=org:acme/dept:eng/user:alice&since=2026-04-01T00:00:00Z" \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice"
# Facts (with bi-temporal as_of)
curl "https://api-v1.cortexdb.ai/v1/facts?scope=org:acme/dept:eng&subject=ent_acme_corp&predicate=deal_stage&as_of=2026-04-15T00:00:00Z" \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice"
# Fact timeline (supersession chain)
curl "https://api-v1.cortexdb.ai/v1/facts/timeline?scope=org:acme/dept:eng&subject=ent_acme_corp&predicate=deal_stage" \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice"
# Beliefs + why
curl "https://api-v1.cortexdb.ai/v1/beliefs?scope=org:acme/dept:eng&about=ent_acme_corp" \
-H "Authorization: Bearer $CORTEX_TOKEN" -H "X-Cortex-Actor: user:alice"
curl "https://api-v1.cortexdb.ai/v1/beliefs/why?belief_id=belief_01HX..." \
-H "Authorization: Bearer $CORTEX_TOKEN" -H "X-Cortex-Actor: user:alice"REST as_of works; the Python SDK facts() does not wrap it
The REST GET /v1/facts?as_of=… bi-temporal query works. Note the Python SDK's facts() wraps only
subject/predicate — for as-of queries from Python, call this endpoint directly. On a content-only
self-hosted instance the Facts/Beliefs layers are empty (need enrichment) — see
Self-hosting defaults.
Selective forget
curl -X POST https://api-v1.cortexdb.ai/v1/forget \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/dept:eng/user:alice",
"layers": ["beliefs"],
"selector": { "predicate": "is_likely_to_renew" },
"cascade": "derived_only",
"audit_note": "User retracted speculation"
}'A selector of {"memory_ids": [...]} purges every listed id. If a storage backend fails mid-delete the
server returns 502 FORGET_BACKEND_FAILED instead of reporting success — deletes are idempotent, so
retry the same call.
REST forget uses selector/audit_note; the SDKs rename these
The REST body accepts selector and audit_note as shown (verified). The SDKs use different
names for the same request: the Python SDK targets by flat keyword
(predicate=, memory_ids=, reason=, no selector); the
TypeScript SDK keeps selector but renames audit_note → reason.
GDPR erasure (preview → execute)
# Preview
curl -X POST https://api-v1.cortexdb.ai/v1/erasures/preview \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{ "scope": "org:acme/user:alice", "audit_note": "DSR #1234 — preview" }'
# Execute (202 on acceptance)
curl -X POST https://api-v1.cortexdb.ai/v1/erasures \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/user:alice",
"from_preview_id": "ervw_01HX...",
"idempotency_key": "erasure-dsr-1234",
"audit_note": "DSR #1234"
}'Backend failures surface as 502 ERASURE_BACKEND_FAILED (retriable — erasure is idempotent), and an
accepted job can still end in status "failed" with an error field — poll the job status rather than
assuming acceptance means completion.
Lifecycle stream (SSE)
curl -N https://api-v1.cortexdb.ai/v1/lifecycle/stream?scope=org:acme/dept:eng \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice" \
-H "Accept: text/event-stream"Use the Last-Event-ID header on reconnect to resume from where you left off.
Capability matrix lookup
curl "https://api-v1.cortexdb.ai/v1/policy/effective?actor=user:alice&scope=org:acme/dept:eng/user:alice" \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: user:alice"policy/effective returns flat capability strings
On v0.9.9 policy/effective returns a flat list of granted capability strings — it does not attach a
per-capability tier or reason. See Policy.
Versioning and SDK compatibility
The API surface is the contract, not the server build. Every route lives under /v1, and the
clients are versioned independently of the server — their release numbers do not track the server's.
A current-generation client works against any v0.9.x server and the managed cloud, because they all
speak the same /v1 surface.
| Client | Package | Current release | Speaks |
|---|---|---|---|
| Python SDK | cortexdbai | 0.11.2 | /v1 |
| TypeScript SDK | cortexdbai | 0.9.3 | /v1 |
| CLI | cortexdb-cli | 0.5.4 | /v1 |
| MCP server | cortexdb-mcp | 0.7.4 | /v1 (22 tools) |
| Connectors | cortexdb-connectors (cortexdb-sync) | 0.2.x | /v1 |
The one real cliff: pre-v1 client releases
Client releases from before the v1 cutover call retired v0 routes and 404 on every request —
verified live: POST /v1/remember and POST /v1/recall_memories both return 404 on v0.9.9. The
worst offender is cortexdb-mcp 0.2.x (mapped to /v1/remember / /v1/recall_memories); pin
cortexdb-mcp>=0.6.0 and current cortexdbai / cortexdb-cli. There is no compatibility problem
in the other direction — a newer client against an older v0.9.x server is fine.
To see exactly what a server is running, call GET /v1/admin/version — it returns the release
version, the crate_version, the git_sha, and the built_at timestamp:
curl https://api-v1.cortexdb.ai/v1/admin/version
# {"version":"v0.9.9","crate_version":"0.9.9","git_sha":"59c5dad…","built_at":"2026-09-03T16:22:02Z"}Health
curl https://api-v1.cortexdb.ai/v1/admin/health
# {"status":"healthy","version":"v0.9.9"}(Unauthenticated.) Health is a pure liveness check — it always returns 200 while the process is up,
and returns only status and version (with a v prefix). Containers and orchestrators should probe
readiness instead: GET /v1/admin/ready returns 200 when the data dir and storage handles are
usable and 503 otherwise, and reports degraded: true plus the pinned embedding_provider when the
server is running on mock embeddings.
Unprefixed aliases GET /v1/health and GET /v1/ready exist and return the same bodies as the
/v1/admin/* versions — handy if your probe config can't reach the admin path.