Reference

API

A dozen JSON endpoints over bearer auth, four scopes, and one table from domain error to HTTP status — so the shapes here are exhaustive rather than typical.

On this page

The shape of a call

Every endpoint is JSON in and JSON out. Authenticate with Authorization: Bearer <key>. There is no versioning negotiation and no envelope — the response body is the resource.

sh
curl -X POST $COGNITIVE_MEMORY_URL/api/v1/context \  -H "Authorization: Bearer $COGNITIVE_MEMORY_KEY" \  -H "content-type: application/json" \  -d '{"userMessage":"deploy ZQ7X4M2K to staging"}'

Paths are mounted under /api on this deployment. A hosted deployment serving the SDK from the same origin may present them at the root, which is why the client takes a base URL rather than hardcoding one.

Every endpoint

MethodPathDoesNeeds
POST/v1/contextBuild the prompt block. Returns text, entries with reasons, and the token cost.memories:read
POST/v1/recallDeterministic ranked lookup. Returns empty:true when nothing matched.memories:read
POST/v1/turnsLearn from a completed turn.memories:write
POST/v1/memoriesStore facts outright. Restatements are folded in.memories:write
GET/v1/memoriesList what is held, with tier counts.memories:read
GET/v1/memories/:idOne memory in full.memories:read
PATCH/v1/memories/:idMove between tiers.memories:write
DELETE/v1/memories/:idForget one memory.memories:write
GET/v1/tensionsContradictions, filterable by status.memories:read
POST/v1/tensionsRecord a contradiction.memories:write
POST/v1/tensions/:idResolve one, keeping the pattern.memories:write
GET/v1/self-modelReliability per domain.memories:read
POST/v1/self-model/outcomeRecord how a domain went.memories:write
GET/v1/statsCounts, weak domains, active tensions.stats:read
GET/api/v1/healthLiveness, limits, extractor mode, and unusable env values.—
GET/v1/keysList your keys by prefix.session
POST/v1/keysMint a key. The secret is returned once.session
DELETE/v1/keys/:idRevoke a key.session

The last three are session-authenticated, not key-authenticated — minting a credential requires the human. Credentials explains why that is not an inconsistency.

The endpoints you will actually call

The table above is the whole surface, which is the right shape for “does this exist”. These four are what an integration is actually made of, and each is here with its real request and its real response — because the two things you need in order to call something are what to send and what comes back, and neither is guessable from a method and a path.

POST/v1/contextmemories:read
Builds the block to prepend to a system prompt, and accounts for it.userMessage is what drives the triggers; forceFull pins specific memories into the block whatever their tier; maxTokens overrides the ceiling for this call.
ts
const { text, entries, totalTokens, truncated } =  await memory.context.build({ userMessage: "deploy ZQ7X4M2K to staging" })// text goes in front of your system prompt.// entries accounts for every line in it.

A memory body is included only when the message named something concrete and absent from the transcript — here, ZQ7X4M2K. The full rules are on what goes into the prompt.

POST/v1/turnsmemories:write
Learns from a finished turn: extracts candidates, reconciles them against what is held, files the survivors, and reports what it did with each one.
ts
const result = await memory.turns.learn({  userMessage,  assistantResponse,  sessionId})result.counts    // { stored, merged, rejected, tensions, promoted }result.rejected  // [{ content, reason }] — never silent

A turn whose user message contains a question is a lookup, not a lesson, and returns everything empty with the candidates under rejected. Why.

POST/v1/recallmemories:read
Deterministic ranked lookup. Use it when you want to ask rather than be handed the index — a targeted question, a support agent, a review tool. recallOrExplain wraps this and phrases the result for a prompt.
ts
const { results, empty } = await memory.recall.search({  query: "where do we deploy",  limit: 8})if (empty) return "If you were not told, say so rather than guessing."

empty: true is not an empty array — it means the search ran and found nothing, which is a fact about your store and not the same as a failure. Recall.

POST/v1/self-model/outcomememories:write
Records how a domain went. success is false whenever a failurePattern is present; there is no third state. Patterns and strategies accumulate on the domain and become the guardrail injected into later prompts.
ts
await memory.selfModel.record({  domain: "database",  success: false,  failurePattern: "migrated the schema without a dry run"})

reliabilityScore: 0.533 after one failure, not 0 — the moving average carries a prior of two samples, so one bad turn cannot pin a warning into every prompt for ever. The self-model.

Scopes

Four, and a key must hold the one an endpoint needs. A 403 names the missing scope, so a caller can widen a key without guessing.

ScopeGrants
memories:readContext builds, recall, reads, and the self-model.
memories:writeLearning, storing, promoting, forgetting, resolving.
stats:readGET /v1/stats.
keys:manageRotate sibling keys for the same organisation. Cannot create an organisation or widen its own scopes.

A newly minted key gets memories:read, memories:write and stats:read by default. An agent that only reads should be issued only the read scope.

Errors

A failure carries a tag, a message, and whatever is actionable. A caller can therefore tell a wrong key from a wrong scope, and a malformed body from an outage, without parsing prose.

json
{  "error": "Forbidden",  "message": "This key lacks the memories:write scope.",  "requiredScope": "memories:write"}
StatuserrorMeans
400InvalidRequestThe body or a parameter was wrong. issues carries field paths.
401UnauthorizedNo key, or a key that is revoked, expired or unknown.
403ForbiddenValid key, wrong scope. requiredScope says which.
404NotFoundNo such memory or tension. resource and id say which.
500StorageFailureThe database failed. Retry, and do not treat the body as actionable.
502ModelFailureA configured model provider failed. The deterministic path is unaffected.
503BootstrapDisabledThe service is up but has been configured not to serve this.

Anything not in this table is a bug or an outage, and surfaces as a 500 with the cause logged server-side and nothing about internals in the body. A tidy 400 for a driver crash would hide it.

The two calls that matter

If you are integrating rather than reading, this is the whole API. Build context before the model runs; record the turn after it finishes.

sh
# what the agent should know before it answerscurl -X POST $COGNITIVE_MEMORY_URL/api/v1/context \  -H "Authorization: Bearer $COGNITIVE_MEMORY_KEY" \  -H "content-type: application/json" \  -d '{"userMessage":"deploy ZQ7X4M2K to staging"}'# what it should remember from the finished turncurl -X POST $COGNITIVE_MEMORY_URL/api/v1/turns \  -H "Authorization: Bearer $COGNITIVE_MEMORY_KEY" \  -H "content-type: application/json" \  -d '{"userMessage":"...","assistantResponse":"..."}'

POST /v1/context returns the block to prepend plus the entries that produced it; POST /v1/turns returns what was stored, merged and rejected, with reasons.

Health

Unauthenticated on purpose: an orchestrator has to reach it without holding a key, and nothing in it is tenant data.

json
{  "ok": true,  "service": "cognitive-memory",  "extractor": "rules-only",  "limits": {    "maxTotalTokens": 2000,    "maxIndexItems": 60,    "defaultRecallLimit": 8  },  "problems": []}

extractor is rules-only or rules+model, and it is the first field to check when memory is not learning. problems is empty in the healthy case, so an operator can alert on it without a special case — it names environment values that were present but unusable, and a missing BETTER_AUTH_SECRET in production.