> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xtrace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Ingest memories

> Async ingest. Returns ``202 + job_id`` by default; the extraction
runs in the background and the caller polls
``GET /v1/memories/jobs/{job_id}``.

With ``?wait=true`` the server holds the connection up to ~30
seconds waiting for extraction to terminate. If it finishes in
time, the response is ``200 OK`` with the terminal job inline
(``status: "succeeded"`` or ``"failed"``). If the wait window
elapses first, the response falls back to ``202 + pending`` and
the caller resumes the normal polling pattern.



## OpenAPI

````yaml https://api.staging.xtrace.ai/openapi.public.json post /v1/memories
openapi: 3.1.0
info:
  title: XTrace Vec DB
  description: XTrace API
  version: 1.0.0
servers:
  - url: https://api.production.xtrace.ai
    description: Production
security:
  - ApiKeyHeader: []
  - BearerToken: []
paths:
  /v1/memories:
    post:
      tags:
        - memories
      summary: Ingest memories
      description: |-
        Async ingest. Returns ``202 + job_id`` by default; the extraction
        runs in the background and the caller polls
        ``GET /v1/memories/jobs/{job_id}``.

        With ``?wait=true`` the server holds the connection up to ~30
        seconds waiting for extraction to terminate. If it finishes in
        time, the response is ``200 OK`` with the terminal job inline
        (``status: "succeeded"`` or ``"failed"``). If the wait window
        elapses first, the response falls back to ``202 + pending`` and
        the caller resumes the normal polling pattern.
      operationId: ingest_memories_v1_memories_post
      parameters:
        - name: wait
          in: query
          required: false
          schema:
            type: boolean
            description: >-
              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.
            default: false
            title: Wait
          description: >-
            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.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IngestRequest'
      responses:
        '200':
          description: >-
            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`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IngestJobResponse'
        '202':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IngestJobResponse'
        '400':
          description: '`detail.code = "invalid_messages"` — empty `messages` array.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: >-
            Authentication failed. Missing / invalid API key. Body carries
            `detail.code = "unauthorized"`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: >-
            Rate-limit or daily-cap exceeded. `Retry-After` and `RateLimit-*`
            response headers indicate when to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            Server-side failure. Body carries `detail.code = "server_error"`
            with a stable error code; details are in the operator log, indexed
            by the `X-Request-Id` echoed in the response header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      x-codeSamples:
        - lang: typescript
          label: TypeScript SDK
          source: |
            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);
components:
  schemas:
    IngestRequest:
      properties:
        messages:
          items:
            $ref: '#/components/schemas/Message'
          type: array
          title: Messages
          description: >-
            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:
          type: string
          minLength: 1
          title: User Id
          description: >-
            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.
          examples:
            - alice
        conv_id:
          type: string
          minLength: 1
          title: Conv Id
          description: >-
            Conversation identifier. REQUIRED. Anchors every extracted memory to
            a conversation for replay, export, and bulk retract.
          examples:
            - conv-2026-05-15-abc
        agent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Agent Id
          description: Optional agent scope. Indexed alongside the other entity ids.
        app_id:
          anyOf:
            - type: string
            - type: 'null'
          title: App Id
          description: Optional app scope. Indexed alongside the other entity ids.
        timestamp_format:
          anyOf:
            - type: string
            - type: 'null'
          title: Timestamp Format
          description: >-
            Optional `strptime` format string used to parse each message's
            `date` field into a real timestamp on the batch-extraction path
            (e.g. `"%Y-%m-%d %H:%M:%S"`). When omitted, or when a `date` value
            fails to parse, the turn keeps its raw date string with no parsed
            timestamp. Only consulted on the batch path (message count above the
            live threshold); the live path ignores it.
          examples:
            - '%Y-%m-%d %H:%M:%S'
        extract_artifacts:
          type: boolean
          title: Extract Artifacts
          description: >-
            When true, extracted artifacts are also stored and returned as
            `artifact` memories, in addition to fact + episode extraction. Off
            by default — most expensive stage and most callers don't need it.
            Routing (live vs batch) is unaffected and still chosen by message
            count.
          default: false
        agentic:
          type: boolean
          title: Agentic
          description: >-
            When true, ingest through the agentic (task-aware) path:
            recall-first extraction + a Stage-2 noise filter + agentic artifact
            detection + a structured session gist. Intended for tool-using /
            coding-agent transcripts. Off by default (conversational
            extraction).
          default: false
        group_ids:
          items:
            type: string
          type: array
          title: Group Ids
          description: >-
            Optional list of group ids the client wants associated with this
            ingest. Each id must be a registered group on the org (see `POST
            /v1/groups`). Ids that are unknown or archived are dropped silently
            and echoed back in `result.ignored_group_ids` — a single stale id
            will not fail the ingest, since the chat path tolerates partial
            tagging better than a hard reject. After extraction, a classifier
            judges each memory personal-vs-shareable: personal memories are
            never group-tagged (they stay in the author's personal scope);
            shareable memories are tagged with every prompt-less (catch-all)
            group in the valid subset, plus whichever prompted groups the
            classifier matched. Tagged rows become reachable via `filters:
            {group_ids: <id>}` on search. The capped length is
            `MAX_GROUP_IDS_PER_INGEST`.
      type: object
      required:
        - messages
        - user_id
        - conv_id
      title: IngestRequest
      description: |-
        POST /v1/memories — async ingest from a message list.

        Required entities: ``user_id`` and ``conv_id``. ``user_id`` scopes
        a user's per-user memory namespace, 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 — the most expensive stage and most callers don't
        need it). Episodes are always extracted (``conv_id`` is
        guaranteed). Facts are always extracted.

        There is no client-facing pipeline hint: the server selects its
        extraction strategy automatically based on the number of messages.
    IngestJobResponse:
      properties:
        object:
          type: string
          const: ingest_job
          title: Object
          description: Constant discriminator for the resource type.
          default: ingest_job
        id:
          type: string
          title: Id
          description: Opaque job id of the form `job_<32-hex-chars>`.
          examples:
            - job_a1b2c3d4e5f6071829304a5b6c7d8e9f
        status:
          type: string
          enum:
            - pending
            - running
            - succeeded
            - failed
          title: Status
          description: >-
            Lifecycle state. `pending` → enqueued. `running` → extraction in
            progress. `succeeded` → `result` is set. `failed` → `error` is set.
            Terminal jobs (`succeeded` / `failed`) are retained for 24h before
            TTL sweep returns 404 `job_not_found`.
        created_at:
          type: string
          format: date-time
          title: Created At
          description: ISO-8601 timestamp the job was created.
        updated_at:
          anyOf:
            - type: string
            - type: 'null'
          format: date-time
          title: Updated At
          description: ISO-8601 timestamp of the most recent state transition.
        result:
          anyOf:
            - $ref: '#/components/schemas/IngestResult'
            - type: 'null'
          description: Populated when `status = succeeded`; null otherwise.
        error:
          anyOf:
            - $ref: '#/components/schemas/IngestJobError'
            - type: 'null'
          description: Populated when `status = failed`; null otherwise.
      type: object
      required:
        - id
        - status
        - created_at
      title: IngestJobResponse
      description: |-
        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.
    ErrorEnvelope:
      properties:
        detail:
          $ref: '#/components/schemas/ErrorDetail'
      type: object
      required:
        - detail
      title: ErrorEnvelope
      description: |-
        Standard error body for non-2xx responses raised by the memory
        API. Pydantic field-validation failures (422) use the FastAPI-
        default ``HTTPValidationError`` shape instead, where ``detail`` is
        an array of per-field error entries — switch on the response
        status code to pick the right shape.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    Message:
      properties:
        role:
          type: string
          title: Role
          description: 'Speaker role: typically `"user"` or `"assistant"`.'
          examples:
            - user
            - assistant
        content:
          type: string
          title: Content
          description: Message text content.
          examples:
            - I like Thai food and spicy dishes.
        date:
          anyOf:
            - type: string
            - type: 'null'
          format: date-time
          title: Date
          description: >-
            Optional ISO-8601 timestamp for the turn. Used by the batch ingest
            path when provided; ignored by the live path.
        dia_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Dia Id
          description: >-
            Optional client-supplied per-turn id (carried for trace/eval
            pipelines).
      type: object
      required:
        - role
        - content
      title: Message
      description: |-
        One chat-style turn in an ingest payload. Order matters: facts
        and episodes are extracted by walking adjacent user→assistant
        pairs (the live path) or the full sequence (the batch path).
    IngestResult:
      properties:
        object:
          type: string
          const: ingest_result
          title: Object
          description: Constant discriminator for the resource type.
          default: ingest_result
        memories_created:
          items:
            $ref: '#/components/schemas/MemoryRef'
          type: array
          title: Memories Created
          description: >-
            Thin references to new facts / episodes / artifacts written this
            ingest. Each entry is `{id, type, text}`; full row shape is at `GET
            /v1/memories/{id}`.
        memories_updated:
          items:
            $ref: '#/components/schemas/MemoryRef'
          type: array
          title: Memories Updated
          description: >-
            Thin references to existing rows whose payload was updated during
            this ingest (e.g. artifact dedup hits).
        memories_superseded_by:
          additionalProperties:
            type: string
          type: object
          title: Memories Superseded By
          description: >-
            Map of `old_fact_id → new_fact_id`. Populated when the extraction
            pipeline detects a contradiction with an existing active fact: the
            old fact's `tag2` flips to superseded and the new fact records
            `supersedes = old_id`.
        stage_timings:
          additionalProperties:
            type: number
          type: object
          title: Stage Timings
          description: Per-stage extraction latencies (seconds).
        ignored_group_ids:
          items:
            type: string
          type: array
          title: Ignored Group Ids
          description: >-
            Subset of the request's `group_ids` that the server dropped because
            the id was unknown or archived. Empty on the common path. Lets
            clients self-correct stale caches without having to re-issue the
            ingest.
      type: object
      title: IngestResult
      description: |-
        Terminal job ``result`` payload, populated when status is
        ``succeeded``. ``memories_created`` and ``memories_updated`` carry
        thin :class:`MemoryRef` entries rather than full ``Memory`` rows
        — see :class:`MemoryRef` for the trade-off rationale and how to
        fetch the full row when needed.
    IngestJobError:
      properties:
        type:
          type: string
          title: Type
          description: Error class — typically `"server_error"` for ingest failures.
          examples:
            - server_error
        code:
          type: string
          title: Code
          description: >-
            Stable error code; switch on this rather than the message. Typically
            `ingest_failed`.
          examples:
            - ingest_failed
        message:
          type: string
          title: Message
          description: >-
            Human-readable, sanitized summary. Safe to log; not safe to switch
            on.
      type: object
      required:
        - type
        - code
        - message
      title: IngestJobError
      description: |-
        Job-level error payload. Mirrors the global error envelope
        shape so clients can branch on ``code`` the same way they do for
        direct request failures.
    ErrorDetail:
      properties:
        code:
          type: string
          title: Code
          description: >-
            Stable error identifier. Switch on this rather than parsing the
            message. Common values: `invalid_request`, `invalid_messages`,
            `memory_not_found`, `job_not_found`, `immutable_field`,
            `reserved_field`, `empty_text_field`, `missing_user_id`,
            `missing_conv_id`, `unsupported_include_option`, `cursor_mismatch`,
            `unauthorized`, `forbidden`, `rate_limited`, `delete_failed`,
            `search_failed`, `ingest_failed`, `server_error`.
          examples:
            - memory_not_found
        message:
          type: string
          title: Message
          description: Human-readable summary; safe to log but not safe to switch on.
          examples:
            - Memory 0fa1c0e6-... not found
      type: object
      required:
        - code
        - message
      title: ErrorDetail
      description: |-
        Inner ``detail`` block on every non-2xx response raised by a
        memory-API route handler. ``code`` is the stable identifier
        clients should switch on; ``message`` is a sanitized human-
        readable summary.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    MemoryRef:
      properties:
        id:
          type: string
          title: Id
          description: Stable UUID of the written / updated row.
        type:
          type: string
          enum:
            - fact
            - artifact
            - episode
          title: Type
          description: >-
            Memory subtype. Determines which detail variant lives behind the id
            when fetched via `GET /v1/memories/{id}`.
        text:
          type: string
          title: Text
          description: >-
            Short readable preview as captured at write time. Same value as
            ``Memory.text`` on the row at the moment of ingest. **Snapshot, not
            live state** — a subsequent supersede (text PATCH) replaces the
            row's current text; the value here keeps reflecting what the ingest
            job wrote. Fetch ``GET /v1/memories/{id}`` for current state.
      type: object
      required:
        - id
        - type
        - text
      title: MemoryRef
      description: |-
        Thin reference to a memory row written or updated by an ingest
        job. Carries only what a client needs for confirmation / per-type
        routing; the full ``Memory`` shape (entity ids, details,
        timestamps, categories) is one ``GET /v1/memories/{id}`` away.

        Trade-off rationale: storing the full ``Memory`` shape inside the
        job record duplicates content that is already persisted — every
        field except ``id`` is derivable via the read endpoints. The thin
        shape keeps the record small while preserving the two fields that
        make a poll response useful on its own: ``type``
        (lets clients route per memory subtype without a follow-up) and
        ``text`` (the human-readable preview, makes the result usable as
        a confirmation receipt).
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
      description: 'Long-lived org API key. Alternative: `Authorization: Bearer <key>`.'
    BearerToken:
      type: http
      scheme: bearer
      bearerFormat: Token
      description: 'Long-lived API key sent as `Authorization: Bearer <api-key>`.'

````