Documentation Index
Fetch the complete documentation index at: https://docs.mem.xtrace.ai/llms.txt
Use this file to discover all available pages before exploring further.
Once a memory is ingested and the job reaches succeeded, it’s immediately queryable. There are two read endpoints:
search — vector + filter ranked retrieval. Use when you have a natural-language query.
list — paginated browse with flat-equality filtering. Use when you don’t have a query (e.g. “all of Alice’s memories”).
Vector search
const results = await client.memories.search({
query: 'what does the user like to eat?',
filters: { user_id: 'alice' },
limit: 10,
});
for (const m of results.data) {
console.log(m.score?.toFixed(2), '·', m.text);
}
Required: query (non-empty string). The server embeds it and ranks results by cosine similarity.
The response is a Stripe-style list envelope:
{
object: 'list',
data: [ /* Memory[], with .score populated */ ],
has_more: false,
next_cursor: null,
extras: null, // populated only when `include` is set
}
Filter DSL
filters is a key→condition map. Top-level keys are implicit-AND.
Flat equality (most common)
{ user_id: 'alice', project: 'atlas' }
// → user_id == "alice" AND project == "atlas"
Operators
// Set membership
{ project: { $in: ['atlas', 'mercury'] } }
// Negation
{ priority: { $ne: 'low' } }
// Range
{ created_at: { $gte: '2026-01-01', $lte: '2026-12-31' } }
{ score: { $between: [0.5, 0.9] } }
Boolean composition
{
AND: [
{ user_id: 'alice' },
{
OR: [
{ project: 'atlas' },
{ project: 'mercury', priority: 'high' },
],
},
],
}
// Negation
{ NOT: { channel: 'internal' } }
Restricting by memory type
type is a first-class filter axis:
{ user_id: 'alice', type: 'fact' } // facts only
{ user_id: 'alice', type: { $in: ['fact', 'episode'] } }
The response carries has_more and next_cursor. The SDK exposes an async iterator that handles pagination automatically:
for await (const memory of client.memories.list({ user_id: 'alice', limit: 50 })) {
// …each memory across all pages
}
For single-page access (when you need cursor-level control), use listPage:
const page = await client.memories.listPage({
user_id: 'alice',
limit: 50,
cursor,
});
Cursors are tenant-scoped — a cursor from one (org, key) pair can’t be used by another.
Retrieving a single memory
const memory = await client.memories.get('5b0d0f7d-d502-45d5-847d-e19512b5c517');
Returns the full row, including details.full_content for artifacts (omitted from list/search responses by default).
Composed retrieval — context_prompt
For agent loops that want the assembled-markdown context to drop directly into an LLM prompt:
const results = await client.memories.search({
query: 'what should I know about the user?',
filters: { user_id: 'alice', conv_id: 'conv_42' },
include: ['context_prompt'],
});
const prompt = results.extras?.context_prompt;
// → "## What we know about the user\n- Vegetarian\n- Loves pad see ew\n- ..."
context_prompt runs an additional retrieval-agent pass over the top results — higher latency, metered separately. It requires both user_id and conv_id in filters.
Including artifact full content
Artifact rows omit details.full_content from list/search responses by default (it can be megabytes). Opt in:
const page = await client.memories.search({
query: 'design doc',
filters: { user_id: 'alice', type: 'artifact' },
include: ['full_content'],
});
No-op for fact and episode rows.
See also
- Ingesting memories — what gets stored that you’re then searching
- API Reference → Memories → Search — every field and operator