Skip to main content
POST
/
v1
/
memories
TypeScript SDK
import { MemoryClient } from '@xtraceai/memory';

const client = new MemoryClient({
  apiKey: process.env.XTRACE_API_KEY!,
  orgId:  process.env.XTRACE_ORG_ID!,
});

const job = await client.memories.ingest({
  messages: [
    { role: 'user', content: 'My favorite food is pad see ew.' },
  ],
  user_id: 'alice',
  conv_id: 'conv_2026_05_16',
});

// Wait for extraction to finish (exp backoff, 60s timeout default)
const done = await client.memories.jobs.pollUntilDone(job.id);
console.log(done.result?.memories_created);
{
  "id": "<string>",
  "created_at": "2023-11-07T05:31:56Z",
  "object": "ingest_job",
  "updated_at": "2023-11-07T05:31:56Z",
  "result": {
    "object": "ingest_result",
    "memories_created": [
      {
        "id": "<string>",
        "text": "<string>"
      }
    ],
    "memories_updated": [
      {
        "id": "<string>",
        "text": "<string>"
      }
    ],
    "memories_superseded_by": {},
    "stage_timings": {}
  },
  "error": {
    "type": "<string>",
    "code": "<string>",
    "message": "<string>"
  }
}

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.

Authorizations

x-api-key
string
header
required

Long-lived org API key. Alternative: Authorization: Bearer <key>.

X-Org-Id
string
header
required

Required alongside the API key (no key→org reverse index).

Headers

X-Service-Token
string | null

Query Parameters

wait
boolean
default:false

When true, hold the connection up to ~30s waiting for extraction to terminate. Returns 200 + terminal job inline on success; falls back to 202 + pending on timeout. Default false → 202 + pending immediately.

Body

application/json

POST /v1/memories — async ingest from a message list.

Required entities: user_id and conv_id. user_id keys xmem's per-user session-cache namespace (one MultiKBSet per (org, user), so a user's facts accumulate in one place rather than fragmenting per-conversation). conv_id anchors every extracted memory to a conversation for replay, export, and bulk retract. agent_id and app_id remain optional — set either, both, or neither. There is no auto-default app_id rule.

extract_artifacts opts in to the artifact-extraction stage (off by default — most expensive stage and most callers don't need it). Episodes are always extracted (conv_id is now guaranteed). Facts are always extracted. (Per SoT decision #4.)

There is no client-facing pipeline hint: the server picks live vs batch by len(messages) (SoT decision #4 + Settings threshold).

messages
Message · object[]
required

Chat-style turns to extract memories from. Must be non-empty — an empty list returns 400 invalid_messages (the route raises this explicitly so the wire error carries the stable code rather than a generic Pydantic validation message). Server picks live vs batch extraction by length; no client-facing strategy hint.

user_id
string
required

User identifier. REQUIRED. Keys the per-user session namespace so a user's facts accumulate across their conversations rather than fragmenting per-conv. Also stored as an indexed filter axis on every row.

Minimum string length: 1
Example:

"alice"

conv_id
string
required

Conversation identifier. REQUIRED. Anchors every extracted memory to a conversation for replay, export, and bulk retract.

Minimum string length: 1
Example:

"conv-2026-05-15-abc"

agent_id
string | null

Optional agent scope. Indexed alongside the other entity ids.

app_id
string | null

Optional app scope. Indexed alongside the other entity ids.

metadata
Metadata · object

Free-form customer metadata. Each key lands as its own indexed payload key, filterable on search. Reserved internal keys (tag1-tag5, kb_type, org_id, etc.) are stripped silently — see the reserved_field PATCH error for the list.

extract_artifacts
boolean
default:false

When true, run the artifact-extraction stage in addition to fact + episode extraction. Off by default — most expensive stage and most callers don't need it. Setting this routes the request through the batch extraction path regardless of message count.

Response

Inline terminal response. Returned when ?wait=true is set and the extraction pipeline completes inside the wait window (~30s). Body is the terminal IngestJobResponse with status: "succeeded" (carrying result) or "failed" (carrying error).

Returned by POST /v1/memories (with status 202 by default, 200 when ?wait=true succeeds inline) and by GET /v1/memories/jobs/{job_id}. Same wire shape across every read of a job's lifecycle.

id
string
required

Opaque job id of the form job_<32-hex-chars>.

Example:

"job_a1b2c3d4e5f6071829304a5b6c7d8e9f"

status
enum<string>
required

Lifecycle state. pending → enqueued. running → extraction in progress. succeededresult is set. failederror is set. Terminal jobs (succeeded / failed) are retained for 24h before TTL sweep returns 404 job_not_found.

Available options:
pending,
running,
succeeded,
failed
created_at
string<date-time>
required

ISO-8601 timestamp the job was created.

object
string
default:ingest_job

Constant discriminator for the resource type.

Allowed value: "ingest_job"
updated_at
string<date-time>

ISO-8601 timestamp of the most recent state transition.

result
IngestResult · object

Populated when status = succeeded; null otherwise.

error
IngestJobError · object

Populated when status = failed; null otherwise.