Brainby arc-labs/docs
TypeScript SDK

Memories — top-level API

Reference for recall.remember, recall.search, recall.correct, recall.explain, recall.forget, and the recall.memory CRUD escape hatch.

remember()

POST/v1/rememberAPI keystable
recall.remember(input: RememberInput, options?: TransportRequestOptions): Promise<RememberResult>;
recall.remember(input: RememberInput & { stream: true }, options?: TransportRequestOptions): Promise<RecallStream<PipelineEvent, RememberResult>>;

Send conversation turns, raw observations, or structured events into the seven-stage write pipeline. The server runs pre_filterextractresolve_refsdedupeconflictpersist, and (when an idempotency key replays) skips straight to replay.

Input — discriminated union

ParameterTypeRequired
messagesArray<{ role: 'user' | 'assistant'; content: string }>optional

Conversation turns. The pipeline runs full extraction and may produce multiple memories per turn.

observationsstring[]optional

Plain pre-distilled facts. Each becomes a single user turn before extraction.

eventsArray<{ description: string; at?: string; tags?: string[] }>optional

Structured events with explicit timestamps. Useful for system events (logins, settings changes).

idempotencyKeystringoptional

Optional client-generated key that lets the server deduplicate replays. When omitted, the SDK auto-generates one per request.

Exactly one of messages, observations, or events is required.

Returns

FieldTypePresence
storedMemoryId[]always
IDs of newly persisted memories.
mergedArray<{ existingId: MemoryId; sourceTurnId: string }>always
Memories that deduplicated to existing rows.
supersededArray<{ newId: MemoryId; oldId: MemoryId }>always
Older memories whose content was overridden by a newer extraction.
discardednumberalways
Count of candidates dropped (junk filter, low-confidence, redundant).
flaggednumberalways
Count flagged for review (e.g. groundedness verification failure).
traceIdstringalways
Server-side trace ID for cross-referencing logs.
latencyMsnumberalways
Wall-clock pipeline duration.
llmCostUsdnumberalways
Estimated LLM cost in USD across the pipeline.

Example

const result = await recall.remember({
  messages: [
    { role: 'user', content: 'I prefer dark mode.' },
    { role: 'user', content: 'I live in Berlin.' },
  ],
});

console.log(`stored=${result.stored.length} merged=${result.merged.length}`);
console.log(`traceId=${result.traceId}`);

When to use

Reach for messages for chat applications. Reach for observations when an upstream system has already distilled facts and you only want extraction + dedup. Reach for events when timestamps matter — login events, settings changes, anything log-flavoured.

Common pitfalls

idempotencyKey is one-shot — the same key + same scope returns the cached result without re-running the pipeline. Do not reuse a key across different bodies; the server will return the original outcome regardless of your new input.

When the pipeline is still running for a previous request with the same key, the server returns 409 REQUEST_IN_FLIGHT. The SDK throws RecallConflictError with retryAfterMs: 1000 so callers can wait and retry safely.

POST/v1/searchAPI keystable
recall.search(input: SearchInput, options?: TransportRequestOptions): Promise<SearchResult>;
recall.search(input: SearchInput & { stream: true }, options?: TransportRequestOptions): Promise<RecallStream<PipelineEvent, SearchResult>>;

Run a hybrid retrieval query. The four-stage read pipeline — understandretrieverankfilter — returns a list of MemoryWithScore, in descending relevance order. See Search for the deep dive; this section is a fast reference.

Input

ParameterTypeRequired
querystringrequired
Natural-language query.
limitnumberoptional
Max memories to return (default 10, clamped to [1, 100]).
filters.memoryTypeMemoryTypeoptional
Restrict to one of fact | preference | event | entity | relation | convention | constraint.
filters.minConfidencenumberoptional
Minimum confidence threshold (default 0.5).

Returns

SearchResult with memories: MemoryWithScore[], totalBeforePolicy, traceId, latencyMs, and plan?. See Search for full field semantics.

Example

const hits = await recall.search({
  query: 'where does the user live?',
  limit: 5,
  filters: { memoryType: 'preference', minConfidence: 0.6 },
});
for (const m of hits.memories) console.log(m.score, m.content);

correct()

POST/v1/feedbackAPI keystable
recall.correct(
  id: MemoryId,
  correction: string | { content: string; reason?: string },
  options?: TransportRequestOptions,
): Promise<CorrectResult>;

Replace a memory's content. The server creates a new memory carrying the corrected content, supersedes the old one (old.supersededBy = new.id), and persists reason to the audit log. The old memory is not deleted — its history is still queryable.

Input

ParameterTypeRequired
idMemoryIdrequired
The memory ID being corrected.
correctionstring | CorrectionInputrequired

A bare string is the new content. Pass an object to include an optional reason for the audit log.

Returns

FieldTypePresence
supersededIdMemoryIdalways
The original memory ID (now superseded).
newMemoryIdMemoryIdalways
The new memory ID carrying the corrected content.
traceIdstringalways
Trace ID for the audit-log entry.
latencyMsnumberalways
Wall-clock latency.

Example

await recall.correct(memoryId, 'user actually lives in Hamburg');
await recall.correct(memoryId, {
  content: 'user lives in Hamburg',
  reason: 'updated via settings page',
});

See Correct & feedback for the supersession chain, audit-log shape, and how this differs from recall.memory.patch.

explain()

GET/v1/memories/:idAPI keystable
recall.explain(id: MemoryId, options?: TransportRequestOptions): Promise<MemoryExplanation>;

Provenance-focused view of a single memory. Returns the content plus where it came from, how often it has been retrieved, and whether it has been superseded.

Returns

FieldTypePresence
idMemoryIdalways
typeMemoryTypealways
contentstringalways
confidencenumberalways
Float in [0, 1].
tagsstring[]always
provenance.accessCountnumberalways
How many times this memory has appeared in search results.
provenance.createdAtstringalways
ISO 8601 timestamp.
provenance.updatedAtstringalways
ISO 8601 timestamp.
provenance.supersededByMemoryId
Set when a newer memory has replaced this one.

When to use

explain() is the right call when you want the why of a memory — for dashboards, "where did this come from?" tooltips, and supersession-chain traversal. For mutating operations use recall.memory.patch().

forget()

POST/v1/forgetAPI keystable
recall.forget(input: ForgetInput, options?: TransportRequestOptions): Promise<ForgetResult>;

Soft-delete one or more memories with an audit reason. Pass hard: true for an irreversible delete. Both forms persist reason to the audit log for compliance / GDPR records.

Input — discriminated union

ParameterTypeRequired
idMemoryIdoptional
Forget exactly one memory.
filter.typeMemoryTypeoptional
Forget all memories of this type.
filter.tagsstring[]optional
Forget memories carrying ALL of these tags (AND).
reasonstringoptional
Audit-log reason. Optional but recommended.
hardbooleanoptional
When true, hard-delete (not recoverable). Default false.

Returns

FieldTypePresence
deletednumberalways
Number of memories deleted.
traceIdstringalways
latencyMsnumberalways

Example

await recall.forget({ id: memoryId, reason: 'user requested via settings' });
await recall.forget({ filter: { type: 'preference' }, hard: true });

recall.memory — CRUD escape hatch

The recall.memory resource exposes direct CRUD against /v1/memories. Use it for dashboards, admin scripts, and any time you want stable cursor-based pagination instead of ranked search.

get(id)

GET/v1/memories/:idAPI keystable
recall.memory.get(id: MemoryId, options?: TransportRequestOptions): Promise<MemoryDetail>;

Returns the full MemoryDetail: content, confidence, optional predicate and object (for typed memories), tags, the bound scope, accessCount, createdAt, updatedAt, and supersededBy? if the memory has been replaced. Throws RecallNotFoundError for unknown IDs.

list(opts)

GET/v1/memoriesAPI keystable
recall.memory.list(opts?: ListMemoryOptions, options?: TransportRequestOptions): PagedRequest<MemorySummary>;

Returns a PagedRequest<MemorySummary>await it for the first page, or for await it to auto-paginate through every memory in scope. See Pagination for the full pattern.

ParameterTypeRequired
typeMemoryTypeoptional
Restrict to a single type.
limitnumberoptional
Page size (default 20, clamped to [1, 100]).
cursorstringoptional
Explicit cursor override (resume an interrupted iteration).
// First page only
const page = await recall.memory.list({ type: 'preference', limit: 50 });
console.log(page.items.length, page.hasMore);

// Auto-paginate everything
for await (const m of recall.memory.list()) {
  console.log(m.id, m.content);
}

patch(id, patch)

PATCH/v1/memories/:idAPI keystable
recall.memory.patch(id: MemoryId, patch: MemoryPatch, options?: TransportRequestOptions): Promise<MemoryDetail>;

Mutate an existing memory in place — content, confidence, or tag additions. This bypasses the supersession-chain semantics of correct(): the audit log records a patch, not a supersede, and there is no newMemoryId.

ParameterTypeRequired
contentstringoptional
New content.
confidencenumberoptional
New confidence in [0, 1].
addTagsstring[]optional
Tags to append (existing tags are preserved).

When to use patch vs correct

Use correct() when the memory's meaning changed — the user updated their address, a preference was wrong, a fact became outdated. The supersession chain preserves history.

Use patch() for mechanical fixups — a typo in content, a confidence score the application now wants to revise, an extra tag for a UI feature. There is no semantic "this replaces that" relationship.

Was this page helpful?

On this page