From 301bac7301327c91863b42fe6b1c0e9abbb7fa0a Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Thu, 12 Feb 2026 14:32:47 +0100
Subject: [PATCH 01/22] Add initial docs
---
docs/engram/api-reference/index.md | 68 ++
docs/engram/concepts.md | 131 ++++
docs/engram/guides/check-run-status.md | 83 +++
docs/engram/guides/index.md | 20 +
docs/engram/guides/manage-memories.md | 64 ++
docs/engram/guides/search-memories.md | 131 ++++
docs/engram/guides/store-memories.md | 121 ++++
docs/engram/index.md | 36 +
docs/engram/quickstart.md | 136 ++++
docusaurus.config.js | 14 +
secondaryNavbar.js | 9 +
sidebars.js | 30 +
static/specs/engram-openapi.json | 928 +++++++++++++++++++++++++
13 files changed, 1771 insertions(+)
create mode 100644 docs/engram/api-reference/index.md
create mode 100644 docs/engram/concepts.md
create mode 100644 docs/engram/guides/check-run-status.md
create mode 100644 docs/engram/guides/index.md
create mode 100644 docs/engram/guides/manage-memories.md
create mode 100644 docs/engram/guides/search-memories.md
create mode 100644 docs/engram/guides/store-memories.md
create mode 100644 docs/engram/index.md
create mode 100644 docs/engram/quickstart.md
create mode 100644 static/specs/engram-openapi.json
diff --git a/docs/engram/api-reference/index.md b/docs/engram/api-reference/index.md
new file mode 100644
index 00000000..7d2b1ddb
--- /dev/null
+++ b/docs/engram/api-reference/index.md
@@ -0,0 +1,68 @@
+---
+title: API reference
+sidebar_position: 4
+description: "Engram REST API overview: authentication, base URL, and interactive API reference."
+---
+
+The Engram API is a REST API for storing, searching, and managing memories.
+
+## Base URL
+
+Your project's API URL is available in the [Weaviate Cloud console](https://console.weaviate.cloud). It follows the format:
+
+```
+https://your-project.engram.weaviate.cloud
+```
+
+## Authentication
+
+Include your API key in the `Authorization` header:
+
+```
+Authorization: Bearer eng_your_api_key
+```
+
+API keys are scoped to a project. All requests authenticated with a key operate within that project's scope. You can create and manage API keys in the Weaviate Cloud console.
+
+## Interactive API reference
+
+The full API reference is generated from the OpenAPI spec and includes request/response schemas, parameter details, and example payloads.
+
+**[Open the interactive API reference](/engram/api-reference/rest)**
+
+## Endpoints
+
+| Method | Path | Description |
+|--------|------|-------------|
+| POST | `/v1/memories` | Store a new memory (async) |
+| GET | `/v1/memories/{id}` | Get a memory by ID |
+| DELETE | `/v1/memories/{id}` | Delete a memory |
+| POST | `/v1/memories/search` | Search memories |
+| GET | `/v1/runs/{run_id}` | Get pipeline run status |
+
+## Error format
+
+All error responses use a consistent format:
+
+```json
+{
+ "status": 400,
+ "message": "error description"
+}
+```
+
+### HTTP status codes
+
+| Code | Description |
+|------|-------------|
+| 400 | Bad request — invalid input or missing required fields |
+| 401 | Unauthorized — missing or invalid API key |
+| 403 | Forbidden — insufficient permissions |
+| 404 | Not found — resource does not exist |
+| 500 | Internal server error |
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/concepts.md b/docs/engram/concepts.md
new file mode 100644
index 00000000..14af0523
--- /dev/null
+++ b/docs/engram/concepts.md
@@ -0,0 +1,131 @@
+---
+title: Concepts
+sidebar_position: 2
+description: "Core concepts in Engram: memories, topics, groups, scoping, pipelines, retrieval types, and runs."
+---
+
+This page explains the core concepts behind Engram's memory system.
+
+## Memories
+
+A memory is a discrete piece of information stored in Engram. Each memory has:
+
+- **Content** — The text of the memory (e.g. "The user prefers dark mode").
+- **Topic** — The category it belongs to (e.g. `user_facts`, `preferences`).
+- **Group** — The memory group that defines how it was processed.
+- **Scope** — The project, user, and conversation it belongs to.
+- **Tags** — Optional string labels for additional classification.
+
+Memories are automatically embedded as vectors, making them searchable by meaning.
+
+## Topics
+
+Topics are named categories within a group. They tell Engram what kinds of information to extract and how to scope it.
+
+Each topic has:
+
+| Property | Description |
+|----------|-------------|
+| `name` | Unique identifier within the group (e.g. `user_facts`) |
+| `description` | Natural language description used in LLM prompts during extraction (e.g. "What food the user likes to eat") |
+| `scoping` | Whether the topic requires a `user_id` and/or `conversation_id` |
+| `is_bounded` | Whether the topic has size constraints |
+
+The topic `description` is important — it's what the extraction pipeline uses to decide how to categorize information. For example, a travel agent might have separate topics with descriptions like "The places the user would like to visit" and "What food the user likes to eat" so the pipeline can route extracted facts to the right topic.
+
+When you create a project, Engram sets up a default group with a default topic automatically.
+
+## Groups
+
+A group bundles a pipeline definition with one or more topics. Each project can have multiple named groups, but most use cases only need the `default` group.
+
+Groups provide:
+
+- A stable UUID identifier for the pipeline configuration
+- Topic definitions that control what gets extracted
+- Pipeline steps that define the processing flow
+- Topic name isolation — different groups can have topics with the same name without collision (e.g. two agents can each have a `user_preferences` topic in separate groups)
+
+## Scoping
+
+Engram uses a multi-level scoping system to isolate memories:
+
+- **Project** — Always required. Every memory belongs to a project, identified by the API key.
+- **User** — Required for user-scoped topics. Memories are strictly isolated between users.
+- **Conversation** — Required when storing to conversation-scoped topics. Optional when searching (see below).
+
+Which scopes are required depends on the topic configuration:
+
+### User-scoped topics
+
+User-scoped topics store memories that belong to an individual user, such as preferences or personal details. Memories are strictly isolated between users — a query for one `user_id` never returns another user's memories. Both storing and searching require the `user_id`.
+
+### Project-wide topics
+
+Topics that are not user-scoped are shared across the entire project. These are useful for procedural memory — things an agent learns about how to perform a task, regardless of which user it is working with. No `user_id` is needed for storing or searching.
+
+### Conversation-scoped topics
+
+Conversation-scoped topics associate memories with a specific conversation. When **storing**, you must provide the `conversation_id`. When **searching**, the `conversation_id` is optional:
+
+- **With `conversation_id`** — Returns only memories from that conversation (e.g. to get a summary of a specific chat).
+- **Without `conversation_id`** — Returns memories across all conversations (e.g. to find everything a user has discussed).
+
+Conversation-scoped topics are typically also user-scoped (e.g. conversation summaries are private to a user).
+
+### Multiple topics in one request
+
+A single request can interact with multiple topics. When it does, the required scope parameters are the union of each topic's requirements. For example, if one topic requires `user_id` and another requires `conversation_id`, the request must include both.
+
+## Pipelines
+
+When you send content to Engram, it runs through an asynchronous pipeline that extracts, transforms, and commits memories. Pipelines are defined as a directed acyclic graph (DAG) of steps.
+
+### Input types
+
+Engram accepts three types of input content:
+
+| Type | Description | Use case |
+|------|-------------|----------|
+| `string` | Raw text | Free-form notes, agent observations |
+| `pre_extracted` | Already-structured content | When you've done your own extraction |
+| `conversation` | Multi-turn messages with roles | Chat transcripts, agent conversations |
+
+### Pipeline steps
+
+Each pipeline processes content through a sequence of steps:
+
+1. **Extract** — Pulls structured memories from the input content. The extraction method depends on the input type (`ExtractFromString`, `ExtractFromConversation`, or `ExtractFromPreExtracted`).
+2. **Transform** — Refines extracted memories using existing context. Steps like `TransformWithContext` and `TransformOperations` deduplicate, merge, and resolve conflicts with existing memories.
+3. **Commit** — Finalizes the operations (create, update, delete) and persists them to storage.
+
+## Retrieval types
+
+Engram supports three search strategies:
+
+| Type | Description | Best for |
+|------|-------------|----------|
+| `vector` | Pure semantic search using embeddings | Finding conceptually related memories |
+| `bm25` | Full-text keyword search | Exact term matching |
+| `hybrid` | Combination of vector and BM25 | General-purpose search (recommended) |
+
+You specify the retrieval type in the `retrieval_config` when searching.
+
+## Runs
+
+Each call to store memories creates a **run** — a trackable unit of pipeline execution. Runs have four possible states:
+
+| Status | Meaning |
+|--------|---------|
+| `running` | Pipeline is actively processing |
+| `in_buffer` | Queued and waiting to start |
+| `completed` | All operations committed successfully |
+| `failed` | An error occurred during processing |
+
+When a run completes, its `committed_operations` field shows exactly which memories were created, updated, or deleted.
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/guides/check-run-status.md b/docs/engram/guides/check-run-status.md
new file mode 100644
index 00000000..590bb272
--- /dev/null
+++ b/docs/engram/guides/check-run-status.md
@@ -0,0 +1,83 @@
+---
+title: Check run status
+sidebar_position: 4
+description: "How to poll pipeline run status and interpret committed operations in Engram."
+---
+
+When you store memories, Engram processes them asynchronously through a pipeline. Each request returns a `run_id` that you can use to track progress.
+
+## Poll a run
+
+```bash
+curl $ENGRAM_API_URL/v1/runs/{run_id} \
+ -H "Authorization: Bearer $ENGRAM_API_KEY"
+```
+
+### Response
+
+```json
+{
+ "run_id": "run-uuid",
+ "status": "completed",
+ "group_id": "group-uuid",
+ "starting_step": 0,
+ "input_type": "string",
+ "error": null,
+ "committed_operations": {
+ "created": [
+ {
+ "memory_id": "memory-uuid-1",
+ "committed_at": "2025-01-01T00:00:01Z"
+ }
+ ],
+ "updated": [],
+ "deleted": []
+ },
+ "created_at": "2025-01-01T00:00:00Z",
+ "updated_at": "2025-01-01T00:00:01Z"
+}
+```
+
+## Run statuses
+
+| Status | Meaning |
+|--------|---------|
+| `running` | Pipeline is actively processing the content |
+| `in_buffer` | Run is queued and waiting to start |
+| `completed` | All operations have been committed successfully |
+| `failed` | An error occurred during processing |
+
+## Committed operations
+
+When a run completes, the `committed_operations` field tells you exactly what changed:
+
+- **`created`** — New memories that were added to storage.
+- **`updated`** — Existing memories that were modified (e.g. merged or refined).
+- **`deleted`** — Memories that were removed (e.g. superseded by an update).
+
+Each entry includes the `memory_id` and a `committed_at` timestamp.
+
+## Handling failures
+
+If a run fails, the `error` field contains a description of what went wrong.
+
+```json
+{
+ "run_id": "run-uuid",
+ "status": "failed",
+ "error": "extraction failed: invalid input format",
+ "committed_operations": null,
+ "created_at": "2025-01-01T00:00:00Z",
+ "updated_at": "2025-01-01T00:00:01Z"
+}
+```
+
+:::tip
+For production systems, implement a polling loop that checks the run status at regular intervals (e.g. every 1-2 seconds) until the status is `completed` or `failed`.
+:::
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/guides/index.md b/docs/engram/guides/index.md
new file mode 100644
index 00000000..af1094df
--- /dev/null
+++ b/docs/engram/guides/index.md
@@ -0,0 +1,20 @@
+---
+title: Guides
+sidebar_position: 3
+description: "Step-by-step guides for storing, searching, and managing memories in Engram."
+---
+
+These guides cover common Engram operations with detailed examples.
+
+## Available guides
+
+- **[Store memories](store-memories.md)** — Send string, pre-extracted, or conversation content to Engram.
+- **[Search memories](search-memories.md)** — Query memories using vector, BM25, or hybrid search with filtering and scoping.
+- **[Manage memories](manage-memories.md)** — Get and delete individual memories by ID.
+- **[Check run status](check-run-status.md)** — Poll pipeline runs and interpret committed operations.
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/guides/manage-memories.md b/docs/engram/guides/manage-memories.md
new file mode 100644
index 00000000..0a691478
--- /dev/null
+++ b/docs/engram/guides/manage-memories.md
@@ -0,0 +1,64 @@
+---
+title: Manage memories
+sidebar_position: 3
+description: "How to get and delete individual memories in Engram by ID."
+---
+
+You can retrieve and delete individual memories using their ID.
+
+## Get a memory
+
+Retrieve a single memory by its ID.
+
+```bash
+curl $ENGRAM_API_URL/v1/memories/{id}?user_id={user-uuid}&topic={topic-name}&group={group-name} \
+ -H "Authorization: Bearer $ENGRAM_API_KEY"
+```
+
+### Query parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `user_id` | string | User scope (required if the topic is user-scoped) |
+| `conversation_id` | string | Conversation scope (required if the topic is conversation-scoped) |
+| `topic` | string | The topic the memory belongs to |
+| `group` | string | The memory group name |
+
+### Response
+
+```json
+{
+ "id": "memory-uuid",
+ "project_id": "project-uuid",
+ "user_id": "user-uuid",
+ "conversation_id": null,
+ "content": "The user prefers dark mode",
+ "topic": "user_facts",
+ "group": "default",
+ "tags": ["preference", "ui"],
+ "created_at": "2025-01-01T00:00:00Z",
+ "updated_at": "2025-01-01T00:00:00Z",
+ "score": null
+}
+```
+
+## Delete a memory
+
+Remove a memory permanently by its ID.
+
+```bash
+curl -X DELETE $ENGRAM_API_URL/v1/memories/{id}?user_id={user-uuid}&topic={topic-name}&group={group-name} \
+ -H "Authorization: Bearer $ENGRAM_API_KEY"
+```
+
+The query parameters are the same as for the get request. You must provide the correct scoping parameters to identify the memory.
+
+:::warning
+Deleting a memory is permanent and cannot be undone.
+:::
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/guides/search-memories.md b/docs/engram/guides/search-memories.md
new file mode 100644
index 00000000..218b0219
--- /dev/null
+++ b/docs/engram/guides/search-memories.md
@@ -0,0 +1,131 @@
+---
+title: Search memories
+sidebar_position: 2
+description: "How to search memories in Engram using vector, BM25, and hybrid retrieval with filtering and scoping."
+---
+
+Search stored memories using the search endpoint:
+
+```
+POST /v1/memories/search
+```
+
+## Basic search
+
+Provide a query and Engram returns the most relevant memories.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories/search \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "query": "What programming language does the user prefer?",
+ "user_id": "user-uuid",
+ "group": "default",
+ "retrieval_config": {
+ "retrieval_type": "hybrid",
+ "limit": 5
+ }
+ }'
+```
+
+```json
+{
+ "memories": [
+ {
+ "id": "memory-uuid",
+ "content": "The user works primarily in Python",
+ "topic": "user_facts",
+ "group": "default",
+ "score": 0.89,
+ "tags": [],
+ "created_at": "2025-01-01T00:00:00Z",
+ "updated_at": "2025-01-01T00:00:00Z"
+ }
+ ],
+ "total": 1
+}
+```
+
+## Retrieval types
+
+Specify the retrieval type in `retrieval_config`:
+
+### Vector search
+
+Pure semantic search using embeddings. Finds memories that are conceptually similar to your query, even without matching keywords.
+
+```json
+{
+ "retrieval_config": {
+ "retrieval_type": "vector",
+ "limit": 10
+ }
+}
+```
+
+### BM25 search
+
+Full-text keyword search. Best for finding memories that contain specific terms.
+
+```json
+{
+ "retrieval_config": {
+ "retrieval_type": "bm25",
+ "limit": 10
+ }
+}
+```
+
+### Hybrid search
+
+Combines vector and BM25 for the best of both approaches. This is the recommended retrieval type for most use cases.
+
+```json
+{
+ "retrieval_config": {
+ "retrieval_type": "hybrid",
+ "limit": 10
+ }
+}
+```
+
+## Filter by topic
+
+Restrict your search to specific topics by providing a `topics` array.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories/search \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "query": "user preferences",
+ "topics": ["user_facts", "preferences"],
+ "user_id": "user-uuid",
+ "group": "default",
+ "retrieval_config": {
+ "retrieval_type": "hybrid",
+ "limit": 10
+ }
+ }'
+```
+
+If you omit `topics`, Engram searches across all topics in the group.
+
+## Scoping
+
+Search results are scoped to match the parameters you provide:
+
+- **`user_id`** — Required for user-scoped topics. Only returns memories for this user.
+- **`conversation_id`** — Optional for conversation-scoped topics. Include it to filter to a single conversation, or omit it to search across all conversations.
+- **`group`** — Search within this group. Defaults to `default`.
+
+:::tip
+For user-scoped topics, always include the `user_id` you used when storing the memories. For conversation-scoped topics, you can omit `conversation_id` to search across all conversations at once.
+:::
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/guides/store-memories.md b/docs/engram/guides/store-memories.md
new file mode 100644
index 00000000..5cfb3f64
--- /dev/null
+++ b/docs/engram/guides/store-memories.md
@@ -0,0 +1,121 @@
+---
+title: Store memories
+sidebar_position: 1
+description: "How to store memories in Engram using string, pre-extracted, or conversation content types."
+---
+
+Engram supports three content types for storing memories. Each triggers a different extraction pipeline.
+
+All requests go to the same endpoint:
+
+```
+POST /v1/memories
+```
+
+## String content
+
+Send raw text and let Engram extract structured memories from it.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "content": {
+ "type": "string",
+ "content": "The user prefers dark mode and works primarily in Python. They are building a RAG application."
+ },
+ "user_id": "user-uuid",
+ "group": "default"
+ }'
+```
+
+The pipeline extracts individual facts from the text (e.g. "prefers dark mode", "works in Python") and stores them as separate memories.
+
+## Pre-extracted content
+
+If you've already extracted structured content, send it directly. This skips the LLM extraction step and goes straight to storage.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "content": {
+ "type": "pre_extracted",
+ "content": "User prefers dark mode",
+ "tags": ["preference", "ui"]
+ },
+ "user_id": "user-uuid",
+ "group": "default"
+ }'
+```
+
+## Conversation content
+
+Send a multi-turn conversation and let Engram extract memories from the full exchange.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "content": {
+ "type": "conversation",
+ "conversation": {
+ "id": "conversation-uuid",
+ "messages": [
+ {
+ "role": "user",
+ "content": "I just moved to Berlin and I am looking for a good coffee shop."
+ },
+ {
+ "role": "assistant",
+ "content": "Welcome to Berlin! Here are some popular coffee shops in the city..."
+ },
+ {
+ "role": "user",
+ "content": "I prefer specialty coffee, not chains."
+ }
+ ]
+ }
+ },
+ "user_id": "user-uuid",
+ "conversation_id": "conversation-uuid",
+ "group": "default"
+ }'
+```
+
+The pipeline analyzes the full conversation and extracts relevant facts (e.g. "lives in Berlin", "prefers specialty coffee").
+
+## Response
+
+All three content types return the same response format:
+
+```json
+{
+ "run_id": "run-uuid",
+ "status": "running"
+}
+```
+
+Use the `run_id` to [check the pipeline status](check-run-status.md).
+
+## Optional parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `user_id` | string | Scope the memory to a specific user |
+| `conversation_id` | string | Scope the memory to a specific conversation |
+| `group` | string | Memory group name (defaults to `default`) |
+| `root` | string | Pipeline root name (for advanced pipeline configurations) |
+
+:::info
+Which parameters are required depends on the topic's scoping configuration. If a topic is user-scoped, you must include `user_id`. If a topic is conversation-scoped, you must include `conversation_id` when storing (it is optional when searching).
+:::
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/index.md b/docs/engram/index.md
new file mode 100644
index 00000000..b048d72b
--- /dev/null
+++ b/docs/engram/index.md
@@ -0,0 +1,36 @@
+---
+title: Engram
+sidebar_label: Introduction
+sidebar_position: 0
+description: "Engram is a memory server for LLM agents and applications that provides persistent, semantically searchable memory through a REST API."
+---
+
+Engram is a memory server for LLM agents and applications. It provides a REST API that automatically extracts, transforms, and stores memories using vector embeddings and LLM-powered processing.
+
+Use Engram to give your agents persistent memory that they can write to and search across conversations, users, and topics.
+
+## Key capabilities
+
+- **Automatic memory extraction** — Send raw text, pre-extracted facts, or full conversations. Engram's pipeline extracts and stores structured memories automatically.
+- **Semantic search** — Find relevant memories using vector similarity, BM25 keyword search, or hybrid retrieval.
+- **Scoped memory** — Organize memories by project, user, and conversation. Topics let you categorize memories within a group (e.g. `user_facts`, `preferences`).
+- **Async processing** — Memory storage runs asynchronously through a pipeline. Poll run status to track when memories are committed.
+
+## How it works
+
+1. You send content to the Engram API (text, pre-extracted data, or a conversation).
+2. Engram runs an async pipeline that extracts, transforms, and commits memories to storage.
+3. You search stored memories using vector, BM25, or hybrid retrieval.
+
+## Get started
+
+- **[Quickstart](quickstart.md)** — Create a project, get an API key, store your first memory, and search it.
+- **[Concepts](concepts.md)** — Understand memories, topics, groups, scoping, and pipelines.
+- **[Guides](guides/index.md)** — Step-by-step instructions for storing, searching, and managing memories.
+- **[API reference](api-reference/index.md)** — Full endpoint documentation with request and response schemas.
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docs/engram/quickstart.md b/docs/engram/quickstart.md
new file mode 100644
index 00000000..853e45ed
--- /dev/null
+++ b/docs/engram/quickstart.md
@@ -0,0 +1,136 @@
+---
+title: Quickstart
+sidebar_position: 1
+description: "Get started with Engram by creating a project, generating an API key, storing a memory, and searching it."
+---
+
+This guide walks you through the core Engram workflow: create a project, get an API key, store a memory, and search for it.
+
+## Prerequisites
+
+- A [Weaviate Cloud](https://console.weaviate.cloud) account
+- `curl` or any HTTP client
+
+## Step 1: Create a project
+
+Every memory in Engram belongs to a project. Create one in the [Weaviate Cloud console](https://console.weaviate.cloud).
+
+Creating a project also sets up a default memory group and topic automatically.
+
+## Step 2: Create an API key
+
+Generate an API key for your project in the Weaviate Cloud console. The full key is only shown once — save it securely.
+
+:::warning
+Copy and store the API key immediately. You cannot retrieve it again after it is displayed.
+:::
+
+The console also provides your project's Engram API URL. Set both as environment variables for the examples below:
+
+```bash
+export ENGRAM_API_URL="https://your-project.engram.weaviate.cloud"
+export ENGRAM_API_KEY="eng_abcdef123456..."
+```
+
+## Step 3: Store a memory
+
+Send content to Engram using the memory API. This example sends a plain text string.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "content": {
+ "type": "string",
+ "content": "The user prefers dark mode and uses VS Code as their primary editor."
+ },
+ "user_id": "user-uuid",
+ "group": "default"
+ }'
+```
+
+Engram processes memories asynchronously. The response includes a `run_id` you can use to check the pipeline status.
+
+```json
+{
+ "run_id": "run-uuid",
+ "status": "running"
+}
+```
+
+## Step 4: Check run status
+
+Poll the run endpoint to confirm your memory has been committed.
+
+```bash
+curl $ENGRAM_API_URL/v1/runs/run-uuid \
+ -H "Authorization: Bearer $ENGRAM_API_KEY"
+```
+
+```json
+{
+ "run_id": "run-uuid",
+ "status": "completed",
+ "committed_operations": {
+ "created": [
+ {
+ "memory_id": "memory-uuid",
+ "committed_at": "2025-01-01T00:00:01Z"
+ }
+ ],
+ "updated": [],
+ "deleted": []
+ },
+ "created_at": "2025-01-01T00:00:00Z",
+ "updated_at": "2025-01-01T00:00:01Z"
+}
+```
+
+## Step 5: Search memories
+
+Search for relevant memories using a natural language query.
+
+```bash
+curl -X POST $ENGRAM_API_URL/v1/memories/search \
+ -H "Authorization: Bearer $ENGRAM_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "query": "What editor does the user prefer?",
+ "user_id": "user-uuid",
+ "group": "default",
+ "retrieval_config": {
+ "retrieval_type": "hybrid",
+ "limit": 5
+ }
+ }'
+```
+
+```json
+{
+ "memories": [
+ {
+ "id": "memory-uuid",
+ "content": "The user prefers dark mode and uses VS Code as their primary editor.",
+ "topic": "default",
+ "group": "default",
+ "score": 0.92,
+ "created_at": "2025-01-01T00:00:01Z",
+ "updated_at": "2025-01-01T00:00:01Z"
+ }
+ ],
+ "total": 1
+}
+```
+
+## Next steps
+
+- Learn about [core concepts](concepts.md) like topics, groups, and pipelines.
+- Explore different ways to [store memories](guides/store-memories.md), including conversations and pre-extracted data.
+- See all [search options](guides/search-memories.md) including vector, BM25, and hybrid retrieval.
+
+## Questions and feedback
+
+import DocsFeedback from '/_includes/docs-feedback.mdx';
+
+
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 5cdb09cb..07fd7048 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -59,6 +59,20 @@ const config = {
},
},
],
+ [
+ "@scalar/docusaurus",
+ {
+ id: "engram-api",
+ label: "",
+ route: "/engram/api-reference/rest",
+ configuration: {
+ spec: {
+ url: "/specs/engram-openapi.json",
+ },
+ hideModels: true,
+ },
+ },
+ ],
[
"@signalwire/docusaurus-plugin-llms-txt",
{
diff --git a/secondaryNavbar.js b/secondaryNavbar.js
index 522e0f5a..8bba03a1 100644
--- a/secondaryNavbar.js
+++ b/secondaryNavbar.js
@@ -107,6 +107,15 @@ const secondaryNavbarItems = {
},
],
},
+ engram: {
+ title: "Engram",
+ icon: "fa fa-brain",
+ description: "Persistent memory for LLM agents and applications",
+ link: "/engram",
+ links: [
+ { label: "Documentation", link: "/engram", sidebar: "engramSidebar" },
+ ],
+ },
integrations: {
title: "Integrations",
icon: "fa fa-puzzle-piece",
diff --git a/sidebars.js b/sidebars.js
index c0b2b0af..2b85df39 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -1524,6 +1524,36 @@ const sidebars = {
"cloud/platform/multi-factor-auth",
"cloud/platform/users-and-organizations",
],
+ engramSidebar: [
+ { type: "doc", id: "engram/index", label: "Introduction" },
+ "engram/quickstart",
+ "engram/concepts",
+ { type: "html", value: "
" },
+ {
+ type: "category",
+ label: "Guides",
+ className: "sidebar-main-category",
+ collapsible: false,
+ link: { type: "doc", id: "engram/guides/index" },
+ items: [
+ "engram/guides/store-memories",
+ "engram/guides/search-memories",
+ "engram/guides/manage-memories",
+ "engram/guides/check-run-status",
+ ],
+ },
+ { type: "html", value: "" },
+ {
+ type: "doc",
+ id: "engram/api-reference/index",
+ label: "API reference",
+ },
+ {
+ type: "link",
+ label: "Interactive API reference",
+ href: "/engram/api-reference/rest",
+ },
+ ],
};
module.exports = sidebars;
diff --git a/static/specs/engram-openapi.json b/static/specs/engram-openapi.json
new file mode 100644
index 00000000..d46fb93a
--- /dev/null
+++ b/static/specs/engram-openapi.json
@@ -0,0 +1,928 @@
+{
+ "components": {
+ "schemas": {
+ "CommittedOperationDTO": {
+ "additionalProperties": false,
+ "properties": {
+ "committed_at": {
+ "description": "When this operation was committed (RFC 3339).",
+ "type": "string"
+ },
+ "memory_id": {
+ "description": "The ID of the affected memory.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "memory_id",
+ "committed_at"
+ ],
+ "type": "object"
+ },
+ "CommittedOperationsDTO": {
+ "additionalProperties": false,
+ "properties": {
+ "created": {
+ "description": "Memories that were created by this run.",
+ "items": {
+ "$ref": "#/components/schemas/CommittedOperationDTO"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "deleted": {
+ "description": "Memories that were deleted by this run.",
+ "items": {
+ "$ref": "#/components/schemas/CommittedOperationDTO"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "updated": {
+ "description": "Memories that were updated by this run.",
+ "items": {
+ "$ref": "#/components/schemas/CommittedOperationDTO"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "ConversationInput": {
+ "additionalProperties": false,
+ "properties": {
+ "created_at": {
+ "description": "When the conversation started (RFC 3339). Defaults to now.",
+ "format": "date-time",
+ "type": "string"
+ },
+ "id": {
+ "description": "Conversation ID. Auto-generated if not provided.",
+ "type": "string"
+ },
+ "messages": {
+ "description": "The messages in this conversation. Must contain at least one message.",
+ "items": {
+ "$ref": "#/components/schemas/MessageInput"
+ },
+ "minItems": 1,
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "metadata": {
+ "additionalProperties": {},
+ "description": "Arbitrary key-value metadata to attach to the conversation.",
+ "type": "object"
+ },
+ "updated_at": {
+ "description": "When the conversation was last updated (RFC 3339). Defaults to now.",
+ "format": "date-time",
+ "type": "string"
+ },
+ "user_id": {
+ "description": "User ID associated with this conversation.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "messages"
+ ],
+ "type": "object"
+ },
+ "CreateMemoryRequest": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/CreateMemoryRequest.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "content": {
+ "$ref": "#/components/schemas/InputContent",
+ "description": "The content to process. Accepts raw text, pre-extracted facts, or a multi-turn conversation."
+ },
+ "conversation_id": {
+ "description": "Conversation ID to scope this memory to. Required when the topic is conversation-scoped.",
+ "type": "string"
+ },
+ "group": {
+ "description": "Pipeline group name. Defaults to 'default'.",
+ "type": "string"
+ },
+ "root": {
+ "description": "Pipeline entry point. Overrides the default starting step.",
+ "type": "string"
+ },
+ "user_id": {
+ "description": "User ID to scope this memory to. Required when the topic is user-scoped.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "content"
+ ],
+ "type": "object"
+ },
+ "ErrorDetail": {
+ "additionalProperties": false,
+ "properties": {
+ "location": {
+ "description": "Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'",
+ "type": "string"
+ },
+ "message": {
+ "description": "Error message text",
+ "type": "string"
+ },
+ "value": {
+ "description": "The value at the given location"
+ }
+ },
+ "type": "object"
+ },
+ "ErrorModel": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/ErrorModel.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "detail": {
+ "description": "A human-readable explanation specific to this occurrence of the problem.",
+ "examples": [
+ "Property foo is required but is missing."
+ ],
+ "type": "string"
+ },
+ "errors": {
+ "description": "Optional list of individual error details",
+ "items": {
+ "$ref": "#/components/schemas/ErrorDetail"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "instance": {
+ "description": "A URI reference that identifies the specific occurrence of the problem.",
+ "examples": [
+ "https://example.com/error-log/abc123"
+ ],
+ "format": "uri",
+ "type": "string"
+ },
+ "status": {
+ "description": "HTTP status code",
+ "examples": [
+ 400
+ ],
+ "format": "int64",
+ "type": "integer"
+ },
+ "title": {
+ "description": "A short, human-readable summary of the problem type. This value should not change between occurrences of the error.",
+ "examples": [
+ "Bad Request"
+ ],
+ "type": "string"
+ },
+ "type": {
+ "default": "about:blank",
+ "description": "A URI reference to human-readable documentation for the error.",
+ "examples": [
+ "https://example.com/errors/example"
+ ],
+ "format": "uri",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "InputContent": {
+ "additionalProperties": false,
+ "properties": {
+ "content": {
+ "description": "The text content. Required for 'string' and 'pre_extracted' types.",
+ "type": "string"
+ },
+ "conversation": {
+ "$ref": "#/components/schemas/ConversationInput",
+ "description": "Conversation payload. Required for 'conversation' type."
+ },
+ "tags": {
+ "description": "Tags to attach to the memory. Only used with 'pre_extracted' type.",
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "The input format: 'string' for raw text, 'pre_extracted' for pre-processed facts, or 'conversation' for multi-turn messages.",
+ "enum": [
+ "string",
+ "pre_extracted",
+ "conversation"
+ ],
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": "object"
+ },
+ "MemoriesListResponseBody": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/MemoriesListResponseBody.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "memories": {
+ "description": "The list of memories.",
+ "items": {
+ "$ref": "#/components/schemas/MemoryResponse"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "total": {
+ "description": "Total number of memories returned.",
+ "format": "int64",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "memories",
+ "total"
+ ],
+ "type": "object"
+ },
+ "MemoryResponse": {
+ "additionalProperties": false,
+ "properties": {
+ "Body": {
+ "$ref": "#/components/schemas/MemoryResponseBody"
+ }
+ },
+ "required": [
+ "Body"
+ ],
+ "type": "object"
+ },
+ "MemoryResponseBody": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/MemoryResponseBody.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "content": {
+ "description": "The stored memory content.",
+ "type": "string"
+ },
+ "conversation_id": {
+ "description": "Conversation ID this memory is scoped to, if any.",
+ "type": "string"
+ },
+ "created_at": {
+ "description": "When this memory was created (RFC 3339).",
+ "format": "date-time",
+ "type": "string"
+ },
+ "group": {
+ "description": "The pipeline group this memory was created in.",
+ "type": "string"
+ },
+ "id": {
+ "description": "Unique identifier for this memory.",
+ "type": "string"
+ },
+ "project_id": {
+ "description": "The project this memory belongs to.",
+ "type": "string"
+ },
+ "score": {
+ "description": "Relevance score. Only present in search results.",
+ "format": "double",
+ "type": "number"
+ },
+ "tags": {
+ "description": "Optional tags associated with this memory.",
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topic": {
+ "description": "The topic this memory belongs to.",
+ "type": "string"
+ },
+ "updated_at": {
+ "description": "When this memory was last updated (RFC 3339).",
+ "format": "date-time",
+ "type": "string"
+ },
+ "user_id": {
+ "description": "User ID this memory is scoped to, if any.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "id",
+ "project_id",
+ "content",
+ "topic",
+ "group",
+ "created_at",
+ "updated_at"
+ ],
+ "type": "object"
+ },
+ "MessageInput": {
+ "additionalProperties": false,
+ "properties": {
+ "content": {
+ "description": "The message text.",
+ "minLength": 1,
+ "type": "string"
+ },
+ "created_at": {
+ "description": "When this message was sent (RFC 3339). Defaults to now.",
+ "format": "date-time",
+ "type": "string"
+ },
+ "role": {
+ "description": "The role of the message sender: 'user', 'assistant', or 'system'.",
+ "enum": [
+ "user",
+ "assistant",
+ "system"
+ ],
+ "type": "string"
+ },
+ "tool_call_metadata": {
+ "$ref": "#/components/schemas/ToolCallMetaInput",
+ "description": "Tool call metadata, if this message is related to a tool invocation."
+ }
+ },
+ "required": [
+ "role",
+ "content"
+ ],
+ "type": "object"
+ },
+ "RetrievalConfigDTO": {
+ "additionalProperties": false,
+ "properties": {
+ "limit": {
+ "default": 10,
+ "description": "Maximum number of results to return (1–100). Defaults to 10.",
+ "format": "int64",
+ "maximum": 100,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "retrieval_type": {
+ "description": "The search method: 'vector' for semantic similarity, 'bm25' for keyword matching, or 'hybrid' for a combination of both.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "retrieval_type",
+ "limit"
+ ],
+ "type": "object"
+ },
+ "RunOutputResponseBody": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/RunOutputResponseBody.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "error": {
+ "description": "Error details, present only when status is 'failed'.",
+ "type": "string"
+ },
+ "run_id": {
+ "description": "Unique identifier for the pipeline run. Use this to poll the run status.",
+ "type": "string"
+ },
+ "status": {
+ "description": "Current run status: 'running', 'completed', 'failed', or 'in_buffer'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "run_id",
+ "status"
+ ],
+ "type": "object"
+ },
+ "RunStatusResponseBody": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/RunStatusResponseBody.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "committed_operations": {
+ "$ref": "#/components/schemas/CommittedOperationsDTO",
+ "description": "Memory operations committed by this run. Present once the run completes."
+ },
+ "created_at": {
+ "description": "When this run was created (RFC 3339).",
+ "format": "date-time",
+ "type": "string"
+ },
+ "error": {
+ "description": "Error details, present only when status is 'failed'.",
+ "type": "string"
+ },
+ "group_id": {
+ "description": "The pipeline group this run belongs to.",
+ "type": "string"
+ },
+ "input_type": {
+ "description": "The type of input that was submitted: 'string', 'pre_extracted', or 'conversation'.",
+ "type": "string"
+ },
+ "run_id": {
+ "description": "Unique identifier for this pipeline run.",
+ "type": "string"
+ },
+ "starting_step": {
+ "description": "The pipeline step index where processing began.",
+ "format": "int64",
+ "type": "integer"
+ },
+ "status": {
+ "description": "Current run status: 'running', 'completed', 'failed', or 'in_buffer'.",
+ "type": "string"
+ },
+ "updated_at": {
+ "description": "When this run was last updated (RFC 3339).",
+ "format": "date-time",
+ "type": "string"
+ }
+ },
+ "required": [
+ "run_id",
+ "status",
+ "group_id",
+ "starting_step",
+ "input_type",
+ "created_at",
+ "updated_at"
+ ],
+ "type": "object"
+ },
+ "SearchMemoriesRequest": {
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "description": "A URL to the JSON Schema for this object.",
+ "examples": [
+ "https://example.com/schemas/SearchMemoriesRequest.json"
+ ],
+ "format": "uri",
+ "readOnly": true,
+ "type": "string"
+ },
+ "conversation_id": {
+ "description": "Conversation ID to filter results by. Required when searching conversation-scoped topics.",
+ "type": "string"
+ },
+ "group": {
+ "description": "Pipeline group name. Defaults to 'default'.",
+ "type": "string"
+ },
+ "query": {
+ "description": "The search query text.",
+ "minLength": 1,
+ "type": "string"
+ },
+ "retrieval_config": {
+ "$ref": "#/components/schemas/RetrievalConfigDTO",
+ "description": "Controls the search method and result limit."
+ },
+ "topics": {
+ "description": "Topic names to search. Defaults to all topics in the group.",
+ "items": {
+ "type": "string"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "user_id": {
+ "description": "User ID to filter results by. Required when searching user-scoped topics.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "query",
+ "retrieval_config"
+ ],
+ "type": "object"
+ },
+ "ToolCallMetaInput": {
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "description": "Unique identifier for this tool call.",
+ "type": "string"
+ },
+ "name": {
+ "description": "The name of the tool or function that was called.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "id"
+ ],
+ "type": "object"
+ }
+ }
+ },
+ "info": {
+ "description": "Persistent memory for LLM agents and applications. Store, search, and manage memories using vector embeddings and LLM-powered processing.",
+ "title": "Engram API",
+ "version": "1.0.0"
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/v1/memories": {
+ "get": {
+ "description": "Returns all memories in the project. Results are scoped to the API key's project.",
+ "operationId": "list-memories",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MemoriesListResponseBody"
+ }
+ }
+ },
+ "description": "OK"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "List memories",
+ "tags": [
+ "Memories"
+ ]
+ },
+ "post": {
+ "description": "Send content to be processed through the memory pipeline. Accepts raw text, pre-extracted facts, or a multi-turn conversation. Processing is asynchronous — use the returned `run_id` to track progress.",
+ "operationId": "create-memory",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateMemoryRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RunOutputResponseBody"
+ }
+ }
+ },
+ "description": "OK"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "Store a memory",
+ "tags": [
+ "Memories"
+ ]
+ }
+ },
+ "/v1/memories/search": {
+ "post": {
+ "description": "Find relevant memories using vector similarity, BM25 keyword matching, or hybrid search. Filter results by topic and scope them to a specific user or conversation.",
+ "operationId": "search-memories",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SearchMemoriesRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MemoriesListResponseBody"
+ }
+ }
+ },
+ "description": "OK"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "Search memories",
+ "tags": [
+ "Memories"
+ ]
+ }
+ },
+ "/v1/memories/{id}": {
+ "delete": {
+ "description": "Permanently remove a memory by its ID. You must provide the correct scoping parameters (`user_id`, `conversation_id`) if the topic requires them.",
+ "operationId": "delete-memory",
+ "parameters": [
+ {
+ "description": "Memory ID",
+ "in": "path",
+ "name": "id",
+ "required": true,
+ "schema": {
+ "description": "Memory ID",
+ "format": "uuid",
+ "type": "string"
+ }
+ },
+ {
+ "description": "User UUID",
+ "explode": false,
+ "in": "query",
+ "name": "user_id",
+ "schema": {
+ "description": "User UUID",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Conversation UUID",
+ "explode": false,
+ "in": "query",
+ "name": "conversation_id",
+ "schema": {
+ "description": "Conversation UUID",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Topic name",
+ "explode": false,
+ "in": "query",
+ "name": "topic",
+ "required": true,
+ "schema": {
+ "description": "Topic name",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Group name (defaults to 'default')",
+ "explode": false,
+ "in": "query",
+ "name": "group",
+ "schema": {
+ "description": "Group name (defaults to 'default')",
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "Delete a memory",
+ "tags": [
+ "Memories"
+ ]
+ },
+ "get": {
+ "description": "Retrieve a single memory by its ID. You must provide the correct scoping parameters (`user_id`, `conversation_id`) if the topic requires them.",
+ "operationId": "get-memory",
+ "parameters": [
+ {
+ "description": "Memory ID",
+ "in": "path",
+ "name": "id",
+ "required": true,
+ "schema": {
+ "description": "Memory ID",
+ "format": "uuid",
+ "type": "string"
+ }
+ },
+ {
+ "description": "User UUID",
+ "explode": false,
+ "in": "query",
+ "name": "user_id",
+ "schema": {
+ "description": "User UUID",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Conversation UUID",
+ "explode": false,
+ "in": "query",
+ "name": "conversation_id",
+ "schema": {
+ "description": "Conversation UUID",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Topic name",
+ "explode": false,
+ "in": "query",
+ "name": "topic",
+ "required": true,
+ "schema": {
+ "description": "Topic name",
+ "type": "string"
+ }
+ },
+ {
+ "description": "Group name (defaults to 'default')",
+ "explode": false,
+ "in": "query",
+ "name": "group",
+ "schema": {
+ "description": "Group name (defaults to 'default')",
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MemoryResponseBody"
+ }
+ }
+ },
+ "description": "OK"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "Get a memory",
+ "tags": [
+ "Memories"
+ ]
+ }
+ },
+ "/v1/runs/{run_id}": {
+ "get": {
+ "description": "Check the status of an asynchronous pipeline run. Returns the current state and, once completed, the list of memory operations that were committed (created, updated, or deleted).",
+ "operationId": "get-run-status",
+ "parameters": [
+ {
+ "description": "The UUID of the pipeline run to check",
+ "in": "path",
+ "name": "run_id",
+ "required": true,
+ "schema": {
+ "description": "The UUID of the pipeline run to check",
+ "format": "uuid",
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RunStatusResponseBody"
+ }
+ }
+ },
+ "description": "OK"
+ },
+ "default": {
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorModel"
+ }
+ }
+ },
+ "description": "Error"
+ }
+ },
+ "summary": "Get a run",
+ "tags": [
+ "Runs"
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
From bc28bd0356c7b2e6c645ba93d4ee3cb04623099e Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Fri, 13 Feb 2026 09:07:04 +0100
Subject: [PATCH 02/22] Update docs
---
docs/engram/_includes/architecture.png | Bin 0 -> 255529 bytes
docs/engram/api-reference/index.md | 53 +------------------------
docs/engram/index.md | 2 +
sidebars.js | 7 +---
static/specs/engram-openapi.json | 23 ++++++-----
5 files changed, 18 insertions(+), 67 deletions(-)
create mode 100644 docs/engram/_includes/architecture.png
diff --git a/docs/engram/_includes/architecture.png b/docs/engram/_includes/architecture.png
new file mode 100644
index 0000000000000000000000000000000000000000..9008be323c9ad88da174fb3e6e7203389e7dd600
GIT binary patch
literal 255529
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