@xtraceai/memory package is the primary supported client. It’s a hand-written wrapper over the HTTP API with idiomatic TypeScript types, an exponential-backoff polling helper, async-iterator pagination, and a typed error hierarchy.
fetch). Works in the browser too.
MemoryClient
The entry point. One client serves the whole org.
Constructor options
| Option | Type | Required | Default | Notes |
|---|---|---|---|---|
apiKey | string | ✓ | — | xtk_... API key |
orgId | string | ✓ | — | Organization id, sent as X-Org-Id header |
baseUrl | string | — | https://api.production.xtrace.ai | Override for staging or self-hosted: e.g. https://api.staging.xtrace.ai |
fetch | typeof fetch | — | globalThis.fetch | Inject a custom fetch (tests, polyfills, instrumentation) |
maxRetries | number | — | 2 | Retries on 5xx (idempotent methods) and 429 (any method); honors Retry-After |
requestIdFactory | () => string | — | req_<uuid> | Override the X-Request-Id generator |
client.memories
All memory operations live here. Returns a Memories instance.
ingest(body, options?)
Submit conversation messages for extraction. Returns an IngestJob.
messages, user_id, conv_id. Optional: agent_id, app_id, group_ids (tag the extracted memories to groups), timestamp_format, extract_artifacts (defaults to true — pass false to skip the artifact-extraction stage).
Options:
wait?: boolean— if true, the server holds the connection up to 30s and returns a terminal job inline. Falls back to async if extraction is still running at 30s.signal?: AbortSignalrequestId?: string
list(query?) → AsyncIterable<Memory>
Auto-paginating async iterator over memories matching the query.
user_id, agent_id, conv_id, app_id, type, limit, order, include.
listPage(query?) → Promise<ListEnvelope<Memory>>
Single-page version of list. Use when you need cursor-level control.
get(id) → Promise<Memory>
Fetch a single memory by id. Returns the full row, including details.full_content for artifacts.
delete(id) → Promise<void>
Hard delete. Removes the point outright — afterwards get 404s, it’s gone from list/search, and a second delete 404s (idempotent by absence). There is no update method: corrections flow through ingest (re-ingesting the corrected statement supersedes the old one).
search(body) → Promise<SearchListEnvelope>
Vector search, scoped by what you pass (user_id / group_ids / agent_id / app_id — all AND-narrow; at least one required). mode defaults to compose.
retrieve(body) → Promise<SearchListEnvelope>
Sugar over search that forces mode: 'compose' — the response’s context carries the LLM-assembled, ready-to-inject prompt.
recall(params, options?) → Promise<RecallResult>
Personal + shared (group) read in one call. Fans out a search per scope, dedupes by id, and renders a single prompt sectioned by Personal + group name (shared lines attributed to their author). This is the combined read that AND-scoping can’t express in a single search.
[{ user_id }, { app_id: 'product-kb' }] reads “alice’s memories OR the product KB,” while { user_id, app_id } in one pool would AND them.
Params: query (required) and pools (≥1 pool — each a ScopePool of { user_id?, group_ids?, agent_id?, app_id? }); optional mode (default compose), limit (default 10).
Options: template? (override the prompt format — see PromptTemplate), render? (supply your own renderer), signal?, requestId?.
renderMemoriesPrompt(memories, opts?) is also exported standalone if you want to format a memory list yourself.
client.memories.jobs
get(jobId) → Promise<IngestJob>
Poll a single ingest job. Use pollUntilDone instead for normal flows.
pollUntilDone(jobId, options?) → Promise<IngestJob>
Polls a job until it reaches succeeded or failed. Exponential backoff starting at 500ms, capped at 5s, default 60s timeout.
timeoutMs?: number— default 60_000initialIntervalMs?: number— default 500maxIntervalMs?: number— default 5_000backoffFactor?: number— default 1.5signal?: AbortSignal
client.groups
Register and manage groups — shared tagging targets. Register a group, then pass its id in ingest({ group_ids }) and read it back with search/recall. See the Groups guide.
| Method | Returns | Notes |
|---|---|---|
create({ name, prompt }) | Group | prompt tells the ingest classifier what belongs to this group |
list() | Group[] | all groups (active + archived) |
get(id) | Group | |
update(id, { name?, prompt?, status? }) | Group | edit / re-prompt; status: 'archived' archives |
archive(id) | Group | soft-archive — drops the group from future ingest tagging |
Error classes
Every API failure is a subclass ofMemoryError. Match on the class for HTTP-status handling and on .code for stable machine-readable error codes from the server.
| Class | HTTP status |
|---|---|
BadRequest | 400 |
Unauthorized | 401 |
Forbidden | 403 |
MemoryNotFound | 404 |
Conflict | 409 |
Unprocessable | 422 |
RateLimited | 429 (adds .retryAfter: number) |
ServerError | 5xx |
status: number— HTTP status codecode: string— stable machine-readable error code (e.g.memory_not_found,org_mismatch)errorType: string— category (e.g.invalid_request_error)requestId: string | undefined— propagate to support / logsdetails: Record<string, unknown> | undefined— error-specific extras
Type exports
The full set of exported types:renderMemoriesPrompt and DEFAULT_PROMPT_TEMPLATE are exported as values (not types):
Memory is a discriminated union — m.type === 'fact' narrows m.details to FactDetails, etc.
Retries and timeouts
| What | Default | Override |
|---|---|---|
| Max retries | 2 | new MemoryClient({ maxRetries: 5 }) |
| Retry-eligible methods on 5xx | GET, HEAD only | n/a |
| Retry-eligible methods on 429 | any | n/a — always retried, honoring Retry-After |
| Backoff | 250ms · 500ms · 1s · … capped at 5s, with jitter | n/a |
Request ids
Every request carries anX-Request-Id header (auto-generated req_<uuid> by default). The server echoes it; the SDK surfaces it on errors via err.requestId. Use it when filing support tickets — it pins down the exact request in our logs.
See also
- Quickstart — install and run end-to-end
- Authentication — credentials, headers, environment selection
- Ingesting memories — async/sync, polling, group tagging
- Searching memories — scoping, modes,
recall - Groups — share memory across users
- API Reference tab — the underlying HTTP endpoints