CortexDB Docs
SDKs & Clients

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…EndpointNotes
Try the API in 60 secondsPOST /v1/auth/signupAnonymous, free-tier, 7-day TTL. No email or card.
Run a service accountPOST /v1/auth/tokensRequires auth.mint capability. 1–24h TTL.
Use your own IdP in productionRegister your signing key with CortexDBYour IdP mints PASETO; CortexDB verifies.

Base URL

EnvironmentURL
Self-hosted (recommended)http://localhost:3141
Cloudhttps://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_notereason.

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.

ClientPackageCurrent releaseSpeaks
Python SDKcortexdbai0.11.2/v1
TypeScript SDKcortexdbai0.9.3/v1
CLIcortexdb-cli0.5.4/v1
MCP servercortexdb-mcp0.7.4/v1 (22 tools)
Connectorscortexdb-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.

On this page