Cortex API

Cortex is a memory API. This page is the OpenAPI-level reference for the six core tools an agent or client actually calls, plus the one thing everyone gets wrong: there are two ways in, and they are not interchangeable transports. Read Two ways to reach Cortex first.

memory.recall memory.remember memory.forget memory.health cortex_search cortex_entity Behaviour & scope

Two ways to reach Cortex

The same memory sits behind two endpoints with different wire protocols and different auth. Pick one deliberately.

 /memory — MCP edge/api/cortex/mcp — broker
Wire protocol Real MCP — JSON-RPC 2.0 (initialize, tools/list, tools/call) Flat JSON — {tool, arguments}. This is not MCP.
Auth accepted Bearer JWT only (RS256 / JWKS) Authorization: ApiKey cndk_v1_… or a signed-in session cookie
Rejects Any non-Bearer credential → 401 (an ApiKey is not a valid RS256 JWT) A body that is not exactly {tool, arguments} → 400 "Only tool and arguments are accepted"
Tools exposed 16 (full registry) 19-entry browser allowlist — includes memory.pin, memory.crystals, memory.archive, memory.export/recover/load/get_pins and the maas.connection.* set; the effective set is whatever the live tools/list returns for your credential
Best for An MCP client that already holds a Bearer JWT (Claude Code via its pre-registered client) Portable, self-serve calls with a cndk_v1 key — curl, Gemini CLI, a ChatGPT Action
Why the broker exists. The /memory edge accepts a Bearer JWT and nothing else. The broker accepts your ApiKey (or session), exchanges it server-side for a short-lived Bearer via an OAuth client-credentials grant, then speaks MCP to /memory on your behalf. Your raw ApiKey never reaches Cortex. The ApiKey is a broker-only credential — presenting it directly to /memory returns 401.

The broker request contract

All curl examples below set CONDUIR_BASE to your deployment's origin and use a placeholder key. Mint your own cndk_v1 key at Your keys. Key format: cndk_v1_<22 chars>_<43 chars>.

# set once — your deployment origin (no trailing slash)
export CONDUIR_BASE="https://your-conduir-host"
export CORTEX_KEY="cndk_v1_xxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Names on the wire are dotted. The registry and tools/list advertise memory.recall, memory.remember, and so on, and the broker sends those dotted names. Some MCP clients display them with underscores (memory_recall) — that is a client display detail, not the API name. The graph tools cortex_search and cortex_entity use underscores as their real wire names.

Response envelope

Every tool at the /memory edge returns the MCP tools/call envelope — { content: [{ type: "text", text: <json> }], structuredContent: <result>, isError: <bool> }. isError is true only when the result carries an in-band error field; a transport degraded is deliberately isError: false. The broker unwraps this and returns the structuredContent under result. The Response shape shown for each tool below is that structuredContent.


The six core tools

These are the agent-callable read/write surface, common to both endpoints. The further tools (memory.export, memory.recover, memory.pin, maas.connection.*) are reachable with an appropriately scoped key or session — authentication with the right scope IS the authorization boundary; there is no separate approval ceremony. What your credential can actually do = the live tools/list — see Behaviour & scope.

memory.recall

endpoint page + live test → memory:read

Recall the most relevant memories for a query, ranked relevance-first. Time words in the query are search text, not a date filter — pass a session_id to switch to a deterministic session transcript instead.

Arguments
NameTypeRequiredRules / enum
querystringrequiredNon-empty. Relevance search text; time words are not a date filter.
max_resultsintegeroptional1–100. Default 10.
session_idstringoptional≤256 chars, single-line. When set, returns a session transcript instead of a ranked recall.
after_tsnumberoptionalUnix seconds ≥0. Only meaningful with session_id.
before_tsnumberoptionalUnix seconds ≥0. Must be ≥ after_ts. Only with session_id.
orderenumoptionalasc | desc. Default desc. Only with session_id.
Response (required: items, degraded, store_status)
Example
curl -sS "$CONDUIR_BASE/api/cortex/mcp" \
  -H "Authorization: ApiKey $CORTEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"memory.recall","arguments":{"query":"pilot decision","max_results":5}}'
{ "result": {
    "items": [
      { "content": "The FTSE100 pilot prospect is confirmed.",
        "entry_id": "mcp_9f3c…", "source_tier": "L3_archive" }
    ],
    "degraded": false,
    "store_status": "populated"
} }

memory.remember

endpoint page + live test → memory:write

Write one durable checkpoint. Idempotent per idempotency_key. Store no secrets and no raw transcripts.

Arguments
NameTypeRequiredRules / enum
contentstringrequiredNon-empty. No secrets, no raw transcripts.
idempotency_keystringrequiredStable and unique per logical write. Reuse only for an exact retry.
content_kindenumoptionaldecision | durable_lesson | fact | observation | session_summary. Default observation.
session_idstringoptional≤256 chars correlation label.
client_event_tsnumberoptionalAdvisory only. Never overrides the server write-time.
sequenceintegeroptional≥0. Same-tick ordering tiebreak.
Response (required: idempotency_key, status, memory_ids)
Example
curl -sS "$CONDUIR_BASE/api/cortex/mcp" \
  -H "Authorization: ApiKey $CORTEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"memory.remember","arguments":{
        "content":"Cross-vendor memory proven via one key.",
        "idempotency_key":"note-2026-08-21-1",
        "content_kind":"decision"}}'
{ "result": {
    "idempotency_key": "note-2026-08-21-1",
    "status": "persisted",
    "memory_ids": ["mcp_1a2b…"]
} }

memory.forget

endpoint page + live test → memory:write

Suppress specific entries from future recall. This is a reversible, tenant-scoped un-index tombstone — it does not destroy the underlying records. Physical hard-deletion is a separate operator-governed capability.

Arguments
NameTypeRequiredRules / enum
entry_idsarray<string>required1–100 non-empty strings, as returned by memory.recall.
reasonstringoptional≤512 chars, single-line. Recorded on the tombstone.
Response (required: status)
Runtime observation. Engagement UAT reports activity_confirmed:false returning systematically live. The code permits both values, so treat "always false" as an observed runtime state, not a contract guarantee — the forget itself is still authoritative.
Example
curl -sS "$CONDUIR_BASE/api/cortex/mcp" \
  -H "Authorization: ApiKey $CORTEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"memory.forget","arguments":{
        "entry_ids":["mcp_9f3c…"],
        "reason":"superseded"}}'
{ "result": {
    "status": "forgotten",
    "requested": 1, "tombstoned": 1,
    "already_forgotten": 0, "not_found": 0, "unknown": 0,
    "results": [ { "entry_id": "mcp_9f3c…", "status": "tombstoned" } ],
    "activity_confirmed": false
} }

memory.health

endpoint page + live test → memory:read

Reachability check only. It does not prove a recall result exists or a write persisted.

Arguments

None. additionalProperties: false, empty properties.

Response
Example
curl -sS "$CONDUIR_BASE/api/cortex/mcp" \
  -H "Authorization: ApiKey $CORTEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"memory.health","arguments":{}}'
{ "result": { "status": "ready" } }

cortex_entity

endpoint page + live test → memory:read

Explore the entity relationship graph. Pure read — no reinforcement, no receipt.

Arguments
NameTypeRequiredRules / enum
querystringrequiredNon-empty.
limitintegeroptional1–100. Default 20.
rel_typestringoptional≤128 chars. Restricts the neighbour edge type.
top_nintegeroptional1–10. Default 3. How many matched entities to expand.
related_limitintegeroptional1–100. Default 20.
Response (required: entities, relationships, degraded)
entity_type is flagged systematically unreliable (F-066). Display it if you must, but do not branch program logic on it.
Example
curl -sS "$CONDUIR_BASE/api/cortex/mcp" \
  -H "Authorization: ApiKey $CORTEX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tool":"cortex_entity","arguments":{"query":"pilot","top_n":3}}'
{ "result": {
    "entities": [
      { "name": "Pilot", "entity_type": "concept", "mention_count": 4,
        "first_seen": 1755648000, "last_seen": 1755734400,
        "related_episodes": ["mcp_9f3c…"] }
    ],
    "relationships": [
      { "source": "Pilot", "target": "Prospect", "type": "associated_with", "weight": 0.72 }
    ],
    "degraded": false
} }

Behaviour & scope

How the API behaves as shipped, so you build against the real contract.

  1. forget is a reversible un-index, not a delete. A tenant-scoped tombstone suppresses entries from recall; the underlying records survive. GDPR erasure is therefore not satisfied by forget alone — hard-deletion is a separate operator capability.
  2. Text-only. Every argument and every stored content field is a string. The wire contract has no binary or object content field.
  3. Operator scope is a ceiling. A self-serve cndk_v1 key is clamped to {memory:read, memory:write, offline_access}. So memory.export, memory.recover, and memory.pin (and the maas.connection.* tools) are operator-only and unreachable with an agent key — those succeed only under the signed-in session path. The nine read/write memory + graph tools are the agent-callable surface.
  4. export is episodic-only, and there is no import. memory.export returns your own episodic memory at full fidelity but omits the persisted portable stores (preferences, procedures, pins, entities) and lists non-portable derived state (embeddings, the entity graph, crystals, deep archive, retrieval indexes) that re-derives on any restore. No import / upload tool exists — cross-model migration is export-out only, and the receiving side re-derives, so recall results change after a restore.
  5. Recall is ranked, and every read is checkable. Recall returns the most relevant memories ranked relevance-first, not a keyed lookup. Every read carries degraded and (on recall) store_status, so a consumer can separate a genuine no-match from a degraded read. An empty result is not a confirmed "nothing there".

Next: Quickstart to wire a client in ~60 seconds, or create a key.