Memory
Memory (cold_memory internally) is Catentio's long-running, cross-session knowledge store — rows an agent's runner injects into context so it doesn't have to re-learn the same fact every run. Each row has a type (e.g. note, pattern, fact), optional agent/scope targeting, and a content field that's what actually gets embedded and recalled. There is no tags array, no markdown-title/body split, and no mem_... id — entry ids are plain integers.
For the runtime memory contract (recall ranking, the embedding path), see API → Memory.
Auth. Every route in this namespace requires the control plane's
x-catentio-sessionheader, which this SDK never sends. NeitherapiKeymode nor Bearer-session mode authenticates here. See SDKs → Auth: what actually works.
Namespace
catentio.memory — five methods:
catentio.memory.stats()
catentio.memory.listEntries(query?)
catentio.memory.getEntry(id)
catentio.memory.createEntry(input)
catentio.memory.updateEntry(id, patch)
No delete, verify, supersede, links, bulk-delete, or graph — the control plane has all of them (POST /v1/memory/entries/{id}/verify, POST .../supersede, GET .../links, POST /v1/memory/entries/bulk-delete, GET /v1/memory/graph), but this SDK doesn't wrap any of them.
Methods
memory.stats
Signature. catentio.memory.stats(token?): Promise<MemoryStats>
import { CatentioSaasClient } from '@forjio/catentio-saas-node';
const catentio = new CatentioSaasClient({ session });
const stats = await catentio.memory.stats();
console.log(`${stats.total} entries`);
Real shape:
interface MemoryStats {
total: number;
by_agent: Array<{ key: string; count: number }>;
by_type: Array<{ key: string; count: number }>;
by_scope: Array<{ key: string; count: number }>;
}
There is no entryCount, tagCount, totalBytes, oldestAt, or newestAt field.
memory.listEntries
Signature. catentio.memory.listEntries(query?, token?): Promise<{ data: MemoryEntryListRow[]; meta: { limit: number; search: boolean; total?: number } }>
Real query parameters — these five, and only these five:
| Field | Type | Notes |
|---|---|---|
q |
string | Full-text/semantic search. When set, rows are ranked by relevance and meta.search is true. |
agent |
string | Filter by agent. |
type |
string | Filter by entry type. |
scope |
string | Filter by scope. |
limit |
integer | 1–200, default 50. |
There is no tag, since, or cursor parameter, and no offset either — when q is omitted, the route is ORDER BY created_at DESC LIMIT :limit with no way to page past the first limit rows except by narrowing agent/type/scope.
const { data, meta } = await catentio.memory.listEntries({ agent: 'gojo', limit: 20 });
for (const e of data) {
console.log(e.id, e.type, e.slug, e.preview);
}
Real list-row shape (note: preview, not the full content):
interface MemoryEntryListRow {
id: number;
type: string;
slug: string | null;
name: string | null;
description: string | null;
preview: string; // content, truncated to 240 chars
agent: string | null;
scope: string;
source_attribution: string | null;
status: string;
always_load: boolean;
last_verified_at: string | null;
meta: Record<string, unknown> | null;
created_at: string;
score: number | null; // only set when `q` was used
}
memory.getEntry
Signature. catentio.memory.getEntry(id, token?): Promise<MemoryEntry>
id is the entry's integer row id. Returns full detail (with content, not preview):
interface MemoryEntry {
id: number;
type: string;
slug: string | null;
name: string | null;
description: string | null;
content: string;
agent: string | null;
scope: string;
source_attribution: string | null;
status: string;
always_load: boolean;
last_verified_at: string | null;
meta: Record<string, unknown> | null;
created_at: string;
updated_at: string;
has_embedding: boolean; // never the raw vector
hit_count: number;
impression_count: number;
hit_rate: number; // computed, 0.0 if no impressions
last_hit_at: string | null;
}
const entry = await catentio.memory.getEntry(84213);
console.log(entry.content);
memory.createEntry
Signature. catentio.memory.createEntry(input, token?): Promise<MemoryEntry>
const entry = await catentio.memory.createEntry({
type: 'note',
content: 'Bang asked 2026-05-11 to keep emoji out of source-controlled files unless explicitly requested.',
slug: 'feedback_no_emoji_in_files',
name: 'No emoji in committed files',
scope: 'shared',
});
Real input fields: content (required, non-empty) and type (required, e.g. note/pattern/fact) are the only two the runtime enforces; slug, name, description, source_attribution, agent, scope (defaults to "shared"), always_load (default false), and meta are all optional. There is no title, body, or tags field — those don't exist. Creation is subject to a cold_memory_entries tier cap.
memory.updateEntry
Signature. catentio.memory.updateEntry(id, patch, token?): Promise<MemoryEntry>
This route is OTP-gated.
PATCH /v1/memory/entries/{id}requiresx-catentio-otp-challengeandx-catentio-otp-codeheaders, which this SDK never sends. Without them the call fails with403 { "otp_required": true, "challenge_id", "scope", "expires_at" }instead of applying the patch. To actually update an entry today, drive the OTP challenge/response yourself against the low-level client and pass the headers explicitly.
await catentio.memory.updateEntry(84213, { content: '...' }, /* still 403s without OTP headers */);
Common patterns
Health-check memory size by agent
const stats = await catentio.memory.stats();
console.log(`${stats.total} entries total`);
for (const row of stats.by_agent) {
console.log(`${row.key}: ${row.count}`);
}
Search recall preview
const { data } = await catentio.memory.listEntries({ q: 'pawpado billing', limit: 10 });
console.log(data.map(e => `- ${e.name ?? e.slug} (score ${e.score?.toFixed(2) ?? 'n/a'})`).join('\n'));
Add a feedback entry from a script
async function recordFeedback(slug: string, lesson: string) {
await catentio.memory.createEntry({
type: 'feedback',
slug: `feedback_${slug}`,
content: `**Captured ${new Date().toISOString()}**\n\n${lesson}`,
});
}
Errors
The real error body is FastAPI's {"detail": ...}. Because the wire response never has an error key, the SDK doesn't parse out a structured code/message — you get CatentioError with a generic HTTP_ERROR code and the HTTP reason phrase as the message.
| Status | Cause |
|---|---|
| 401 | Missing/invalid session — this SDK never sends x-catentio-session. |
| 400 | createEntry() missing content or type. |
| 403 | updateEntry() without OTP headers (see above), or past the cold_memory_entries tier cap on createEntry(). |
| 404 | getEntry()/updateEntry() with an id that doesn't exist. |
Next
- Agents — the principal that recalls memory.
- API → Memory — HTTP-level reference including recall semantics and the routes this SDK doesn't wrap.