Skip to content

Memory API

Base path: /api/memory

All endpoints require authentication. See API Overview for auth details.

MethodPathDescription
GET/api/memory/episodesList episodes
GET/api/memory/episodes/{id}Get an episode
POST/api/memory/episodesCreate an episode (inject memory)
DELETE/api/memory/episodes/{id}Delete a single episode
GET/api/memory/episodes/{id}/relatedGet related episodes
GET/api/memory/searchFull-text and semantic search
GET/api/memory/clustersGet memory clusters
GET/api/memory/graphGet the knowledge graph
GET/api/memory/statsGet memory statistics
POST/api/memory/consolidate/conversationsStart a knowledge-graph rebuild job
POST/api/memory/generate-episodesStart a job that generates and embeds episodes
GET/api/memory/jobs/{kind}Status of a memory job (generate or consolidate)
POST/api/memory/backfill-entitiesBackfill entity data for episodes
GET/api/settings/memoryGet memory settings
PUT/api/settings/memoryUpdate memory settings
GET/api/settings/memory/exportExport memory data as JSON
DELETE/api/settings/memory/dataClear all memory data
GET /api/memory/episodes

Query parameters:

ParameterDescription
limitMax results (default: 20, max: 100)
offsetPagination offset (default: 0)
cluster_idFilter by cluster ID
agent_idFilter by agent ID

Response:

{
"episodes": [
{
"id": "ep_abc123",
"content": "User asked about FastAPI dependency injection...",
"summary": null,
"source": "conversation",
"valence": 0.24,
"importance": 0.6,
"access_count": 3,
"created_at": "2026-03-02T14:00:00Z",
"updated_at": "2026-03-02T14:00:00Z"
}
],
"total": 1247,
"limit": 20,
"offset": 0
}
GET /api/memory/episodes/{id}
POST /api/memory/episodes
{
"content": "Note: always use the snippbot_ prefix for database tables",
"importance": 0.7,
"valence": 0.0,
"topics": ["conventions"],
"agent_id": "agent_abc123"
}

Only content is required. All other fields are optional.

Use this to inject persistent instructions or context into an agent’s memory.

DELETE /api/memory/episodes/{id}

Deletes a single episode by ID.

GET /api/memory/episodes/{id}/related

Returns episodes related to the given episode.

GET /api/memory/search?q=database+migration+strategy&mode=hybrid&limit=10

Searches across all episodes using the selected mode. Pass natural language queries to find conceptually related memories.

Query parameters:

ParameterDescription
qSearch query string (required)
modeSearch mode: hybrid (default), keyword, or semantic
limitMax results (default: 10, max: 100)
offsetPagination offset (default: 0)
start_dateISO 8601 date filter (since)
end_dateISO 8601 date filter (until)
sourceFilter by episode source (e.g., conversation, task, insight)

Response:

{
"query": "database migration strategy",
"mode": "hybrid",
"limit": 10,
"offset": 0,
"results": [
{
"episode": {
"id": "ep_abc123",
"content": "User asked about database migration strategy...",
"summary": null,
"valence": 0.0,
"importance": 0.6,
"topics": [],
"entities": [
{ "id": "ent_xyz", "name": "Sql", "type": "concept", "mentions": 2 }
],
"source": "conversation",
"created_at": "2026-03-01T10:00:00Z"
},
"score": 0.92,
"source": "conversation",
"highlights": ["...we decided on a **database migration strategy** that..."]
}
],
"total": 3
}
GET /api/memory/clusters

Returns memory clusters — groups of related episodes identified by the memory system.

Query parameters:

ParameterDescription
nNumber of clusters to compute (default: 4, range: 2-8)
agent_idFilter clusters for a specific agent

Clusters are computed from episode embeddings, and laid out by the optional memory visualization pack (scikit-learn + UMAP; see feature packs). When there are no clusters, the response says why in empty_reason:

empty_reasonMeaning
no_episodesThe agent has no memories yet
embeddings_unavailableThis install has no embedding backend (normally included); repair_command has the fix
visualization_unavailableThe memory visualization pack isn’t installed
missing_embeddingsSome memories have no embedding yet. POST /api/memory/generate-episodes embeds them
too_few_embeddingsEverything is embedded, but there are too few memories to cluster

The response also includes episodes_total, episodes_embedded, embeddings_installed, repair_command (the non-destructive repair command for this install), and visualization: {pack_id, installed, complete}.

GET /api/memory/graph

Returns the full knowledge graph (entities and relationships).

POST /api/memory/consolidate/conversations

Starts a background job that uses the LLM to extract entities and relations from episodes into the knowledge graph. Episodes that were already extracted and haven’t changed are skipped. Extraction makes one LLM call per 5 episodes, so a run can take several minutes.

ParameterDescription
agent_idOnly process this agent’s episodes
wipetrue clears the graph first and re-extracts everything

Returns 202 with {"job": {...}}, or 409 with the running job if a rebuild is already in progress. Poll GET /api/memory/jobs/consolidate for progress and the result.

POST /api/memory/generate-episodes

Starts a background job that creates episodes from conversation history. It then embeds every episode that has no embedding yet, including older ones stored while the embedding model was unavailable. Exchanges that are already stored as episodes are skipped, so running it again only adds new ones. agent_id limits it to one agent.

Returns 202 with {"job": {...}}, or 409 if a job is already running. Poll GET /api/memory/jobs/generate. The finished job’s result has episodes_created, episodes_skipped, embeddings_generated, and a warning if embeddings are unavailable.

GET /api/memory/jobs/{kind}

kind is generate or consolidate. Returns {"job": null} if no job has run since the daemon started. Otherwise it returns the latest job:

{
"job": {
"id": "memjob_1a2b3c4d5e6f",
"kind": "consolidate",
"agent_id": "steven",
"params": { "wipe": false },
"status": "running",
"started_at": "2026-09-25T16:35:24+00:00",
"finished_at": null,
"progress": { "phase": "extracting", "done": 10, "total": 42 },
"result": null,
"error": null
}
}

status is running, done, or error. Only one job of each kind runs at a time. Job state is kept in memory, so it is cleared when the daemon restarts.

POST /api/memory/backfill-entities

Backfills entity extraction for episodes that were created before entity extraction was enabled.

Memory settings are managed under the Settings API, not under /api/memory:

GET /api/settings/memory
PUT /api/settings/memory
GET /api/settings/memory/export

Returns a JSON file containing all episodes and metadata as a download.

DELETE /api/settings/memory/data

Deletes all episodes, the vector index, and the knowledge graph. This action is irreversible.

Response:

{
"cleared": 4821
}
GET /api/memory/stats
{
"total_episodes": 4821,
"total_entities": 142,
"total_relations": 389,
"avg_importance": 0.54,
"avg_valence": 0.12,
"importance_distribution": [12, 45, 89, 210, 380, 920, 1100, 800, 180, 85],
"source_distribution": { "conversation": 4200, "task": 400, "insight": 221 },
"entity_type_distribution": { "language": 12, "framework": 18, "tool": 30, "concept": 82 },
"recall_feedback": { "total_recalls": 320, "used_count": 210, "ignored_count": 110, "usage_rate": 0.66 }
}

Optional features that Snippbot installs into its own environment after the user reviews exactly what will change. The packs are memory-visualization (Nebula clusters) and browser-chromium (the Chromium build the browser tool drives). The package list for each pack is fixed on the server; clients only choose the pack.

MethodPathDescription
GET/api/featuresEvery optional feature with its status, plus the packs and the install kind (backs Settings → Features)
GET/api/features/packsEvery pack and whether it is installed
GET/api/features/packs/{id}/planWhat installing would do on this machine (admin)
POST/api/features/packs/{id}/installStart the install job (admin). 202 {job}, or 409 if one is running
GET/api/features/packs/{id}/installThe latest install job and the pack’s status

GET /api/features returns:

  • features: each feature’s id, title, kind (base, pack or extra), source (the pack id or extra name), status (ok, missing, not-installed or unsupported), unsupported_reason, note, and command. command is what a person runs to get a feature that isn’t there: the repair command for a missing base feature, or the version-pinned extra for an opt-in one. It is null when there is nothing to run.
  • packs: the same objects as GET /api/features/packs
  • install: kind (pipx, uv-tool, editable, venv or system), label, and the installed version
  • repair_command: the non-destructive repair command for this install

The plan runs pip install --dry-run against Snippbot’s own interpreter. It returns:

  • packages: each package with its version, action (new, upgrade, downgrade or reinstall), current_version and size_bytes
  • downloads: non-pip downloads such as Chromium, with their sizes
  • total_bytes
  • skipped: optional packages with no build for this platform, with the reason
  • command: the exact command Snippbot will run (pipx inject, uv pip install or pip install, always --only-binary=:all:)
  • blocked_reason and manual_command: set when Snippbot can’t install here, for example into a system Python marked externally managed

The install job reports progress.phase (installing, downloading or verifying) and progress.log, which is the tail of the installer output. When it finishes, the new modules can be imported immediately; no restart is needed.