Memories
A Memory is an AI-generated summary of a segment of a chat conversation. Each Memory carries a short and a long form of the summary plus extracted facts (topics, decisions, actions, entities) and a vector embedding used for semantic recall during later turns of the conversation.
Memory routes are guarded by chat ownership rather than a permission. The
Memory read_access scope joins the parent chat on the requester's username,
so a Memory is visible only to the owner of its chat. There is no
permission.ai.manage or BYOK gate on these routes.
A Memory's summary is not a single summary string. The persisted shape is:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Memory id |
chatId | string (UUID) | Parent chat id |
title | string | Short title for the Memory |
l1 | string | Detailed summary, Markdown |
l2 | string | One- or two-sentence summary |
topics | string[] | Extracted topics |
decisions | string[] | Extracted decisions |
actions | string[] | Extracted action items |
entities | string[] | Extracted entities (names, files, systems) |
vector | string | null | Embedding stored as text (null until embedded) |
reason | string | Why the Memory was created (see below) |
createdAt / updatedAt | date | Timestamps |
Creation reasons (reason enum):
user_created- default for an explicit create with no other reasonuser_requested- requested by the user during the conversationcompleted_discussion- a discussion reached its natural conclusionmade_decision- a decision was recordedsolved_problem- a problem was resolvednatural_break- a natural break in the conversationtopic_change- the conversation shifted to a new topicautosave- created automatically by the autosave policy
GET /api/chats/{id}/memories
List a chat's Memories, ordered by createdAt ascending. Each Memory carries an
aggregated messageCount (the number of messages assigned to it).
Authentication: Required (chat ownership)
Pre-blocks: chat.lookup
Response:
A HAL collection under _embedded["inf:memory"]. Each item includes the Memory
fields above plus messageCount. vector is returned as text (or null when
the Memory has not been embedded).
[
{
"permissions": {},
"id": "00000000-0000-4000-8000-000000000002",
"chatId": "00000000-0000-4000-8000-000000000001",
"title": "Q4 revenue targets",
"l1": "## Overview\nThe team reviewed **Q4 revenue targets** and growth strategies for the Order Desk.",
"l2": "Reviewed Q4 revenue targets and growth strategies.",
"vector": null,
"topics": [
"Revenue",
"Growth"
],
"decisions": [
"Raise the Q4 target to $2.5M"
],
"actions": [
"Draft the revised forecast"
],
"entities": [
"Order Desk",
"Q4_report.pdf"
],
"reason": "topic_change",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"messageCount": 2
}
]
POST /api/chats/{id}/memories
Create a Memory from the chat's most recent un-memorized messages. The summary is always AI-generated; you cannot supply your own summary text.
Authentication: Required (chat ownership)
Pre-blocks: chat.lookup
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
messageId | string | No | Summarize up to and including this message |
asOf | date | No | Summarize messages up to this timestamp |
reason | string | No | Creation reason (default user_created) |
There is no messageIds array and no summary field. The server selects the
un-memorized messages itself and generates the summary and embedding.
Example Request:
{ "reason": "user_requested" }
Response:
Responds 200 with the created Memory.
A Memory needs at least a user/assistant pair of messages that have not already
been assigned to a Memory. With nothing new to summarize, the route answers
400 No new messages to create memory from before making any AI call.
{
"reason": "user_created"
}
{
"statusCode": 400,
"error": "Bad Request",
"message": "No new messages to create memory from"
}
The success path calls a live model provider to generate the summary and the embedding, so it is not captured here.
GET /api/memories/{id}
Retrieve a single Memory together with its assigned messages. Each message is returned in UI-message shape, with any attached files embedded in the message metadata.
Authentication: Required (chat ownership)
This route is itself the memory.lookup server method. Access is enforced by
the Memory read_access scope, so an unknown or not-owned id answers 404.
Response:
{
"_links": {
"self": {
"href": "https://informer.example.com/api/memories/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000001",
"chatId": "00000000-0000-4000-8000-000000000002",
"title": "Q4 revenue targets",
"l1": "## Overview\nThe team reviewed **Q4 revenue targets** and growth strategies for the Order Desk.",
"l2": "Reviewed Q4 revenue targets and growth strategies.",
"vector": null,
"topics": [
"Revenue",
"Growth"
],
"decisions": [
"Raise the Q4 target to $2.5M"
],
"actions": [
"Draft the revised forecast"
],
"entities": [
"Order Desk",
"Q4_report.pdf"
],
"reason": "topic_change",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"messages": [
{
"id": "00000000-0000-4000-8000-000000000003",
"role": "user",
"parts": [
{
"text": "What were our Q4 targets?",
"type": "text"
}
],
"metadata": {
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"liked": null,
"memoryId": "00000000-0000-4000-8000-000000000001",
"explanation": null,
"files": []
}
},
{
"id": "00000000-0000-4000-8000-000000000004",
"role": "assistant",
"parts": [
{
"text": "The Q4 targets were $2.5M in net revenue.",
"type": "text"
}
],
"metadata": {
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"liked": null,
"memoryId": "00000000-0000-4000-8000-000000000001",
"explanation": null,
"files": []
}
}
]
}
A Memory id that does not exist (or that the requester does not own) answers
404:
{
"statusCode": 404,
"error": "Not Found",
"message": "Memory not found"
}
PUT /api/memories/{id}
Regenerate a Memory's summary and embedding from its assigned messages.
Authentication: Required (chat ownership)
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Must be regenerate |
Example Request:
{ "action": "regenerate" }
Response:
Responds 200 with an envelope, not the raw Memory:
{
"success": true,
"message": "Memory regenerated successfully",
"memory": {
"id": "…",
"l1": "…",
"l2": "…",
"topics": ["…"],
"decisions": ["…"],
"actions": ["…"],
"entities": ["…"],
"createdAt": "…"
}
}
Regenerate to refresh a summary after messages were edited, or to re-embed with an updated model.
Regeneration calls a live model provider for the summary and embedding, so the response is not captured here.
DELETE /api/memories/{id}
Delete a Memory. Its messages remain; only the Memory record and its embedding are removed.
Authentication: Required (chat ownership)
The handler resolves the Memory through the read_access scope and answers
404 for an unknown or not-owned id.
Response:
Responds 200 with { "success": true } (the Informer convention). This route
does not return 204 No Content.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/memories/00000000-0000-4000-8000-000000000001"
}
},
"success": true
}
POST /api/chats/{id}/memories/search
Semantic search over a chat's Memories using vector similarity. The query text is embedded with the same model used to embed the Memories.
Authentication: Required (chat ownership)
Pre-blocks: chat.lookup
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search text (1-500 characters) |
limit | integer | No | Max results (1-20, default 5) |
Example Request:
{ "query": "revenue growth strategies", "limit": 5 }
Response:
Responds 200 with an envelope. Each result is a flattened Memory with a
relevance score, not a nested { memory, score } pair:
{
"success": true,
"query": "revenue growth strategies",
"totalResults": 1,
"results": [
{
"id": "…",
"rank": 1,
"summary": "…",
"details": "…",
"topics": ["…"],
"decisions": ["…"],
"actions": ["…"],
"entities": ["…"],
"messageId": "…",
"createdAt": "…",
"relevance": 0.87
}
]
}
summary is the Memory's l2, details is its l1, and relevance is
derived from the vector distance (null when no distance is available). Higher
relevance indicates a closer match.
Memory search injects relevant prior discussion back into a conversation, improving response quality on recurring topics.
Search embeds the query with a live model provider, so the response is not captured here.