Skip to content

HTTP API reference ​

Derived from server/routes/ and server/models.py. The running server also serves Swagger UI at /docs, which is generated from the same models — use it when you want to try a request rather than read about one.

Base URL — http://localhost:8000 under the shipped compose file.

Auth — every route except /health requires the API key, as either header:

Authorization: Bearer $SODAMEM_API_KEY
X-API-Key: $SODAMEM_API_KEY

Scope — request bodies embed the four scope fields; user_id is required. See Scoping.

Route index ​

methodpathwhat it does
GET/healthliveness; unauthenticated, touches no store
POST/v1/memoriesingest messages, extract facts
POST/v1/memories/batchingest up to 500 sessions in one call
GET/v1/memorieslist memories in a scope
GET/v1/memories/{id}one memory
PATCH/v1/memories/{id}supersede with a new version
DELETE/v1/memories/{id}archive, or purge when enabled
POST/v1/searchranked hits, zero LLM calls
GET/POST/v1/contextprompt-ready block with citations, zero LLM calls
POST/v1/answerplanner + reader answer path
GET/v1/eventsevery add, supersede and delete, with its reason
GET/v1/entity_timelineone entity's history in order
GET/v1/explorewalk the graph outward from a starting point
POST/v1/refinerefine a search
POST/v1/maintenance/dreamrebuild entity profiles
GET/v1/jobs/{id}status of an async job
GET/v1/metricsper-route latency percentiles
GET/v1/usagecumulative LLM token spend
GET/metricsPrometheus exposition
GET/v1/admin/configeffective configuration
GET/POST/v1/admin/keyslist / mint named API keys
DELETE/v1/admin/keys/{id}revoke a named key
GET/v1/admin/requestsrolling request log
GET/v1/admin/statsdisk and workload shape

Writing ​

POST /v1/memories ​

bash
curl -X POST localhost:8000/v1/memories \
  -H "Authorization: Bearer $SODAMEM_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "user_id": "alice",
    "session_id": "s1",
    "session_time": "2023-05-25",
    "messages": [{"role": "user", "content": "Actually I moved from Kauai to Oahu."}]
  }'
fieldtypenotes
messagesarray, min 1{role: user|assistant|system, content}
session_idstringgroups turns into one conversation
session_timestring / numberwhen the conversation happened
inferboolrun extraction (default) or store as given
async_modeboolreturn a job_id instead of blocking

Synchronous responses return facts_extracted, spans_written, turns_written. Async returns {job_id, status: "pending", session_id} — poll GET /v1/jobs/{id}.

Writing needs a model

Extraction calls the configured LLM provider. Reads do not. See Environment variables · LLM.

POST /v1/memories/batch ​

Same shape, but sessions is an array of up to 500 entries. The response carries succeeded, failed, and a per-item results array with the index, so one bad session does not fail the batch.

PATCH /v1/memories/{id} ​

A correction, not an edit. Closes the old version with a valid_until, writes a new one, links them with SUPERSEDES, and returns both ids.

DELETE /v1/memories/{id} ​

Archives by default: the memory leaves search and context, the record and its provenance stay. ?purge=true physically erases and cascades — only when SODAMEM_ALLOW_PURGE=true, and it is irreversible.

Reading ​

POST /v1/search ​

bash
curl -X POST localhost:8000/v1/search \
  -H "Authorization: Bearer $SODAMEM_API_KEY" -H "Content-Type: application/json" \
  -d '{"user_id":"alice","query":"favorite color","top_k":10}'

top_k is 1–100, default 10. Returns hits[] with id, content, score, session_id, occurred_at, metadata, plus a degraded[] array naming any retrieval leg that could not run — an empty vector index degrades the result rather than failing the request.

/v1/context ​

Takes a JSON body on POST, and also answers a plain GET with query parameters, since it is a pure read.

bash
curl -X POST localhost:8000/v1/context \
  -H "Authorization: Bearer $SODAMEM_API_KEY" -H "Content-Type: application/json" \
  -d '{"user_id":"alice","query":"what do they prefer?","token_budget":1000}'

Returns a prompt-ready block, already deduplicated and inside token_budget, with the citations behind every line. Zero LLM calls, and there is no parameter that can change that — the organizer tier is Python-only on purpose.

POST /v1/answer ​

The planner loop. Costs tokens; use it for questions build_context cannot answer in one shot.

Graph ​

routereturns
GET /v1/entity_timelineone entity's history in order, each item still pointing at its source
GET /v1/explorethe graph walked outward from a starting point
POST /v1/refinea refined search over a prior result

Audit ​

GET /v1/events returns every add, supersede and delete with its reason. This is what makes "why did the agent forget X" answerable after the fact.

Jobs ​

GET /v1/jobs/{id} — job records live in the control-plane database (/data/.control/), so status survives a restart: a job in flight during a deploy no longer answers 404.

Maintenance ​

POST /v1/maintenance/dream rebuilds entity profiles. Idempotent, resumable, and safe to overlap — a second call while one is running returns status:"already_running" and does nothing. The response carries remaining_stale, so an hourly cron converges without anyone picking a batch size.

bash
curl -X POST localhost:8000/v1/maintenance/dream \
  -H "Authorization: Bearer $SODAMEM_API_KEY" -H "Content-Type: application/json" \
  -d '{"user_id":"u1","async_mode":true}'

Admin ​

See Self-hosting · Operating it. Named keys are for attribution, not isolation: there are no roles and no per-key scopes.

Errors ​

Errors return {code, message, details}. code is stable and meant to be matched on; message is meant to be read.

Apache-2.0 licensed.