> ## 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.

# Procedural-memory recall (pre-tool-call hook)

> Procedural-memory recall for a pre-tool-call hook.

Fires the symbol tripwire on the in-flight ``action`` (or explicit ``entities``)
and returns the ``lesson``/``procedure`` insights past sessions recorded about
those symbols — advisory, not a mandate. ``data: list[Memory]`` carries the matched
rows (``type: "lesson" | "procedure"``); ``mode=compose`` additionally runs an LLM
relevance gate over ``task`` and assembles a markdown block into ``context``, while
``mode=retrieve`` returns the raw matched rows and leaves ``context`` null. Scope is
enforced server-side from the request's scope axes (``user_id`` / ``group_ids`` /
``agent_id`` / ``app_id``).

Unlike ``POST /v1/memories/search``, this endpoint is **not** metered against the
monthly quota — the hook is meant to be called freely before every tool use.



## OpenAPI

````yaml https://api.staging.xtrace.ai/openapi.public.json post /v1/memories/trigger
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/trigger:
    post:
      tags:
        - memories
      summary: Procedural-memory recall (pre-tool-call hook)
      description: >-
        Procedural-memory recall for a pre-tool-call hook.


        Fires the symbol tripwire on the in-flight ``action`` (or explicit
        ``entities``)

        and returns the ``lesson``/``procedure`` insights past sessions recorded
        about

        those symbols — advisory, not a mandate. ``data: list[Memory]`` carries
        the matched

        rows (``type: "lesson" | "procedure"``); ``mode=compose`` additionally
        runs an LLM

        relevance gate over ``task`` and assembles a markdown block into
        ``context``, while

        ``mode=retrieve`` returns the raw matched rows and leaves ``context``
        null. Scope is

        enforced server-side from the request's scope axes (``user_id`` /
        ``group_ids`` /

        ``agent_id`` / ``app_id``).


        Unlike ``POST /v1/memories/search``, this endpoint is **not** metered
        against the

        monthly quota — the hook is meant to be called freely before every tool
        use.
      operationId: trigger_memory_v1_memories_trigger_post
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TriggerRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResponse'
        '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 — missing firing signal (`action`/`entities`), no
            scope axis, or invalid `include`/`mode`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '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: '`detail.code = "trigger_failed"` — auth resolution failure.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  schemas:
    TriggerRequest:
      properties:
        action:
          anyOf:
            - $ref: '#/components/schemas/ActionContext'
            - type: 'null'
          description: >-
            The in-flight tool call the agent is touching right now — the firing
            signal for recall. Greppable identifiers are extracted server-side
            (`tool` name, `args` values, optional `output`) and matched exactly
            against directives' `trigger_entities`. Supply this or `entities`.
        entities:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: Entities
          description: >-
            Escape hatch: pre-extracted greppable identifiers to fire on,
            bypassing server-side extraction from `action`. Use when the client
            already knows the exact symbols the agent is touching.
          examples:
            - - tool_loop.py
              - AbortError
        include:
          items:
            type: string
            enum:
              - lesson
              - procedure
          type: array
          uniqueItems: true
          title: Include
          description: >-
            Which procedural corpora to recall. Defaults to both
            (`lesson`/`procedure`); subset to restrict (e.g. `["lesson"]`).
        task:
          anyOf:
            - type: string
              maxLength: 2000
            - type: 'null'
          title: Task
          description: >-
            The agent's current goal, in one line. Fed to the relevance gate
            under `mode=compose` so it keeps only the insights that help with
            THIS task.
          examples:
            - fix finalize-on-abort in the tool loop
        mode:
          type: string
          enum:
            - retrieve
            - compose
          title: Mode
          description: >-
            Pipeline depth selector.


            - `compose` (default) — symbol-tripwire recall **plus** an LLM
            relevance-gate step that keeps only the insights relevant to `task`,
            then assembles a markdown block. `data` carries the gated rows;
            `context` carries the markdown. One LLM call.

            - `retrieve` — recall only. No LLM, cheaper and faster. `data`
            carries the matched rows; `context` is null.
          default: compose
        user_id:
          anyOf:
            - type: string
            - type: 'null'
          title: User Id
          description: >-
            Scope key. When supplied, applied as an AND pin so reads are
            tenant-isolated at the storage filter layer. Optional: omit it and
            pass `group_ids` to read a shared group across users. At least one
            of `user_id` / `agent_id` / `app_id` / `group_ids` is required.
          examples:
            - user-123
        group_ids:
          items:
            type: string
          type: array
          title: Group Ids
          description: >-
            Optional group tags — another AND scope axis. When non-empty,
            matched rows must be tagged to at least one of the requested
            group(s). Group ids are server-generated unguessable handles (see
            `POST /v1/groups`); knowing the id is the access boundary.
          examples:
            - - grp_a1b2c3d4e5f6071829304a5b6c7d8e9f
        agent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Agent Id
          description: >-
            Optional agent scope. When set, ANDs onto the active primary scope
            (whether that's `user_id` or `group_ids`): matched rows must also
            carry this exact `agent_id`.
          examples:
            - bot-7
        app_id:
          anyOf:
            - type: string
            - type: 'null'
          title: App Id
          description: >-
            Optional app scope. When set, ANDs onto the active primary scope
            (like `agent_id`): matched rows must also carry this exact `app_id`.
          examples:
            - app-3
      type: object
      title: TriggerRequest
      description: >-
        POST /v1/memories/trigger — procedural-memory recall for a pre-tool-call
        hook.


        An agent calls this right before (or just after) a tool runs to pull the

        ``lesson``/``procedure`` insights that past sessions recorded about the
        symbols

        it's touching — what worked, what didn't. Recall fires on the symbol
        tripwire,

        never a semantic query: the server extracts greppable identifiers from
        ``action``

        (``tool`` / ``args`` / ``output``) or takes them

        straight from ``entities``, and matches them exactly against directives'

        ``trigger_entities``.


        What comes back is advisory, not a mandate — ranked :class:`Memory` rows

        (``type: "lesson" | "procedure"``) in the shared
        :class:`SearchResponse`, with the

        assembled markdown in ``context`` under ``mode="compose"``.


        Scope follows "scope by what you pass": every axis you supply ANDs;
        ``org_id``

        is derived server-side from your API key. At least one of ``user_id`` /
        ``agent_id`` / ``app_id`` /

        ``group_ids`` is required — an unscoped org-wide recall is rejected.
    SearchResponse:
      properties:
        object:
          type: string
          const: search
          title: Object
          description: Constant discriminator for the resource type.
          default: search
        mode:
          type: string
          enum:
            - retrieve
            - compose
          title: Mode
          description: >-
            Echoes the request's mode. `compose` ⇒ both `data` and `context`
            populated; `retrieve` ⇒ `data` populated and `context` null.
        data:
          items:
            $ref: '#/components/schemas/Memory'
          type: array
          title: Data
          description: >-
            Ranked Memory rows from retrieval. Populated for both modes.
            Semantic rows (`fact`/`artifact`/`episode`) are ordered by `score`
            desc; when `include` requests `lesson`/`procedure`, those directive
            rows are precision-gated (not vector-scored, `score` is null) and
            lead the list ahead of the semantic rows. For `mode=compose`, the
            semantic rows are the candidates considered by the context-selection
            step — a (possibly larger) superset of what ended up in `context`.
        context:
          anyOf:
            - type: string
            - type: 'null'
          title: Context
          description: >-
            Assembled markdown context block ready for prompt insertion.
            Populated only when `mode=compose`; null otherwise.
        stage_timings:
          additionalProperties:
            type: number
          type: object
          title: Stage Timings
          description: >-
            Per-stage retrieval-pipeline latencies (seconds). Populated in both
            modes.
        context_selection_applied:
          type: boolean
          title: Context Selection Applied
          description: >-
            True when the LLM context-selection step ran. False indicates the
            pipeline fell through (e.g. an empty candidate pool, or context
            selection disabled by config).
          default: false
      type: object
      required:
        - mode
      title: SearchResponse
      description: |-
        POST /v1/memories/search response.

        ``data`` is **always** populated — both modes return the ranked
        Memory rows from retrieval. ``context`` is populated **only when
        ``mode='compose'``** with the assembled markdown block from the
        LLM context-selection step. ``stage_timings`` is always present
        for per-stage latency attribution.

        Note that under ``mode='compose'`` the rows in ``data`` are the
        **retrieval candidates** — a possibly larger set than the subset
        the LLM selected and wove into ``context``. Useful for showing
        "everything we found" alongside "what we sent to the model."
    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.
    ActionContext:
      properties:
        tool:
          anyOf:
            - type: string
            - type: 'null'
          title: Tool
          description: Tool / MCP name about to run (e.g. "Edit", "linode_api").
        args:
          anyOf:
            - type: object
            - type: 'null'
          title: Args
          description: 'Intended tool arguments (e.g. {"file_path": "tool_loop.py"}).'
        output:
          anyOf:
            - type: string
            - type: 'null'
          title: Output
          description: Most-recent tool output, if firing post-call.
      type: object
      title: ActionContext
      description: >-
        The in-flight tool call the agent is touching — the firing signal for
        procedural

        recall on ``POST /v1/memories/trigger``. The server extracts greppable
        identifiers

        from ``tool`` / ``args`` / ``output`` and matches them

        exactly against directives' ``trigger_entities``. At a pre-tool-call
        hook the

        high-value signal is ``tool`` + ``args`` (the intended call); ``output``
        is for

        firing off a just-returned result.
    Memory:
      properties:
        id:
          type: string
          title: Id
          description: Stable UUID for this memory row.
        object:
          type: string
          const: memory
          title: Object
          description: Constant discriminator for the resource type.
          default: memory
        type:
          type: string
          enum:
            - fact
            - artifact
            - episode
            - lesson
            - procedure
          title: Type
          description: >-
            Memory subtype. `fact` = a single semantic claim extracted from a
            turn; `artifact` = a structured object (code, doc, image) referenced
            by the conversation; `episode` = a session-scoped summary of a
            stretch of turns; `lesson` / `procedure` = a situated directive
            recalled by the symbol tripwire (see `DirectiveDetails`).
        text:
          type: string
          title: Text
          description: >-
            Short readable preview. For facts: the claim statement. For
            artifacts: a summary or title (full body lives in
            `details.full_content`, opt-in via `include=full_content`). For
            episodes: a summary of the session.
        user_id:
          anyOf:
            - type: string
            - type: 'null'
          title: User Id
          description: User scope this row belongs to.
        agent_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Agent Id
          description: Agent scope, if any.
        conv_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Conv Id
          description: Conversation anchor.
        app_id:
          anyOf:
            - type: string
            - type: 'null'
          title: App Id
          description: App scope, if any.
        group_ids:
          items:
            type: string
          type: array
          title: Group Ids
          description: >-
            Group ids associated with this row — the sharing axis. Stamped at
            ingest time and editable afterward via `PATCH /v1/memories/{id}`
            (`add_group_ids` / `remove_group_ids`). Reachable as a filter axis
            via `filters: {group_ids: <id>}` or `{group_ids: {"$in": [<id>,
            ...]}}`.
        categories:
          items:
            type: string
          type: array
          title: Categories
          description: Optional category labels from the extraction pipeline.
        score:
          anyOf:
            - type: number
            - type: 'null'
          title: Score
          description: >-
            Vector-similarity score. Present **only** on search responses; null
            on list / get-by-id / patch / ingest result rows.
        created_at:
          anyOf:
            - type: string
            - type: 'null'
          format: date-time
          title: Created At
          description: ISO-8601 timestamp of original ingest.
        updated_at:
          anyOf:
            - type: string
            - type: 'null'
          format: date-time
          title: Updated At
          description: ISO-8601 timestamp of the last supersede / consolidation.
        details:
          oneOf:
            - $ref: '#/components/schemas/FactDetails'
            - $ref: '#/components/schemas/ArtifactDetails'
            - $ref: '#/components/schemas/EpisodeDetails'
            - 0dc16cfe-7f7d-4424-b72b-94b908c810ef
          type: object
          title: Details
          description: >-
            Type-specific extension. Shape depends on `type` — see `FactDetails`
            / `ArtifactDetails` / `EpisodeDetails` schemas. Always an object;
            never null.
      type: object
      required:
        - id
        - type
        - text
      title: Memory
      description: |-
        Unified memory resource — facts, artifacts, episodes marshal to
        this shape. ``type`` is the discriminator; ``details`` is the
        type-specific extension.

        Invariant: ``text`` is always a short readable preview, regardless
        of type (fact statement / artifact summary / episode summary).
    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.
    FactDetails:
      description: |-
        Per-row fact details — sits under ``Memory.details`` when
        ``Memory.type == "fact"``.
      properties:
        fact_type:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Fact Type
        status:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Status
        supersedes:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Supersedes
        source_role:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Source Role
        episode_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Episode Id
        artifact_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Artifact Id
        artifact_ids:
          items:
            type: string
          title: Artifact Ids
          type: array
        source_event_ids:
          items:
            type: string
          title: Source Event Ids
          type: array
      title: FactDetails
      type: object
    ArtifactDetails:
      description: |-
        Per-row artifact details under ``Memory.details`` when
        ``Memory.type == "artifact"``.

        ``full_content`` is opt-in — omitted on list/search by default,
        included on ``GET /v1/memories/{id}`` and when
        ``include=["full_content"]`` is set on list/search.
      properties:
        title:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Title
        rationale:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Rationale
        version:
          anyOf:
            - type: integer
            - type: 'null'
          default: null
          title: Version
        root_id:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Root Id
        source_fact_ids:
          items:
            type: string
          title: Source Fact Ids
          type: array
        episode_ids:
          items:
            type: string
          title: Episode Ids
          type: array
        full_content:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Full Content
      title: ArtifactDetails
      type: object
    EpisodeDetails:
      description: |-
        Per-row episode details under ``Memory.details`` when
        ``Memory.type == "episode"``.
      properties:
        title:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Title
        started_at:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Started At
        ended_at:
          anyOf:
            - type: string
            - type: 'null'
          default: null
          title: Ended At
        fact_ids:
          items:
            type: string
          title: Fact Ids
          type: array
        artifact_ids:
          items:
            type: string
          title: Artifact Ids
          type: array
      title: EpisodeDetails
      type: object
  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>`.'

````