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

# Memory (Beta)

> Memory turns the review decisions your users make in your app into a searchable knowledge base. Query it over REST to search past decisions, ask questions, and get grounded suggestions.

Your users comment on and review work inside your app. Memory records every one of those decisions, plus the ones your AI agents make. Add your own guidelines on top, then query all of it over REST.

## How it works

<Steps>
  <Step title="Reviews become judgments">
    Every review decision your users make in your app is recorded as a **judgment**: the decision, the reasoning, who decided, and what they decided on. This happens automatically. There is no "create judgment" endpoint.
  </Step>

  <Step title="You add knowledge">
    Ingest your guidelines, standards, and policy docs. Memory converts each file to markdown, extracts rules, and embeds it.
  </Step>

  <Step title="You query both">
    Use `search` for raw decision records, `ask` for a written answer with citations, and `suggest` for a recommendation on a new item.
  </Step>

  <Step title="Memory derives insights">
    Reviewer profiles, decision patterns, stats, and alerts build up from those judgments over time.
  </Step>
</Steps>

<Note>
  A new workspace starts empty. `ask` returns an empty `answer` with `confidence: 0` until enough review history exists. Memory returns nothing rather than inventing an answer.
</Note>

## Quickstart

Every endpoint is a `POST` under `https://api.velt.dev/v2/`. The examples below read your API key and [Auth Token](/docs/security/auth-tokens) from `$VELT_API_KEY` and `$VELT_AUTH_TOKEN`.

<Steps titleSize="h3">
  <Step title="Add a knowledge source">
    Base64-encode a file and send it. Ingestion runs in the background and returns a `sourceId`.

    ```bash theme={null}
    curl -X POST https://api.velt.dev/v2/memory/knowledge/ingest \
      -H "x-velt-api-key: $VELT_API_KEY" \
      -H "x-velt-auth-token: $VELT_AUTH_TOKEN" \
      -H "content-type: application/json" \
      -d '{
        "data": {
          "source": "inline",
          "file": {
            "base64": "'"$(base64 -w0 brand-guidelines.pdf)"'",
            "mimeType": "application/pdf",
            "fileName": "brand-guidelines.pdf",
            "fileSize": 184320
          }
        }
      }'
    # -> { "result": { "status": "processing", "sourceId": "src_9a8...", "message": "Knowledge ingestion task enqueued." } }
    ```

    Poll with that `sourceId` until `status` is `completed` or `failed`:

    ```bash theme={null}
    curl -X POST https://api.velt.dev/v2/memory/knowledge/ingest-status \
      -H "x-velt-api-key: $VELT_API_KEY" \
      -H "x-velt-auth-token: $VELT_AUTH_TOKEN" \
      -H "content-type: application/json" \
      -d '{ "data": { "sourceId": "src_9a8..." } }'
    # -> { "result": { "status": "completed", "extractedRulesCount": 7 } }
    ```

    Files up to 5 MB go inline. For files up to 30 MB, call [Get Upload URL](/docs/api-reference/rest-apis/v2/memory/knowledge/upload-url), `PUT` the bytes to the returned `uploadUrl`, then ingest with `{ "source": "fileRef", "fileRef": "<gs:// URI>", "mimeType": "..." }`.
  </Step>

  <Step title="Ask a question">
    You get a written answer, the records it drew on, and a confidence score.

    ```bash theme={null}
    curl -X POST https://api.velt.dev/v2/memory/ask \
      -H "x-velt-api-key: $VELT_API_KEY" \
      -H "x-velt-auth-token: $VELT_AUTH_TOKEN" \
      -H "content-type: application/json" \
      -d '{ "data": { "question": "How do we handle copy that makes medical claims?" } }'
    ```

    ```json theme={null}
    {
      "result": {
        "answer": "Reviewers reject copy that makes medical claims without a citation...",
        "citations": [ { "recordId": "act_8f3...", "snippet": "Claim 'clinically proven' lacks a citation." } ],
        "confidence": 0.74,
        "recordsSearched": 18
      }
    }
    ```
  </Step>

  <Step title="Search past decisions">
    Use `search` when you want the underlying records instead of a written answer.

    ```bash theme={null}
    curl -X POST https://api.velt.dev/v2/memory/search \
      -H "x-velt-api-key: $VELT_API_KEY" \
      -H "x-velt-auth-token: $VELT_AUTH_TOKEN" \
      -H "content-type: application/json" \
      -d '{ "data": { "query": "unsupported medical claim", "limit": 5, "filters": { "decision": "rejected" } } }'
    ```

    Each result carries the `reasoning`, `decision`, `confidence`, who decided (`actionUser`), and a `similarity` score.
  </Step>

  <Step title="Get a suggestion">
    Ask what Memory would recommend for a new item, based on precedent.

    ```bash theme={null}
    curl -X POST https://api.velt.dev/v2/memory/suggest \
      -H "x-velt-api-key: $VELT_API_KEY" \
      -H "x-velt-auth-token: $VELT_AUTH_TOKEN" \
      -H "content-type: application/json" \
      -d '{ "data": { "query": "Ad copy: clinically proven to reduce wrinkles" } }'
    ```

    ```json theme={null}
    {
      "result": {
        "primary": { "recommendation": "reject", "confidence": 0.78, "basedOn": 12, "topReasons": ["Unsupported medical claim"], "caveats": [] },
        "conflict": null
      }
    }
    ```

    `primary` is the best-grounded recommendation. `conflict` is filled in when the evidence is split, so you can show that reviewers disagree.
  </Step>
</Steps>

## Narrow your results

Add these fields to `search` and `ask` to control what Memory reads.

| Field                  | What it does                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `organizationId`       | Limits the read to one organization.                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `documentId`           | Limits the read to one document. Applied only when you also send `organizationId`; without it the field is ignored and the read stays workspace-wide.                                                                                                                                                                                                                                                                                                   |
| `limit`                | `search` only. Caps results. 1 to 50, defaults to 10. `ask` sizes its own retrieval and ignores this field.                                                                                                                                                                                                                                                                                                                                             |
| `filters`              | Narrows by `decision`, `judgeType` (`human` or `agent`), `contentType`, `reviewerId`, or `dateRange`. `decision` matches the stored value exactly: `comment`, `resolve`, `approved`, `rejected`, `in_progress`, `agree`, `disagree`, `endorse`, `document_approved`, or `document_rejected`. `dateRange` is `{ start, end }` as either ISO-8601 strings or epoch milliseconds. `excludeDocumentIds` leaves out judgments from up to 20 named documents. |
| `filters.annotationId` | Reads one comment thread in chronological order. Requires `organizationId`.                                                                                                                                                                                                                                                                                                                                                                             |
| `recencyDays`          | Returns the last N complete UTC days of activity instead of a semantic match. 1 to 365. Good for digests.                                                                                                                                                                                                                                                                                                                                               |

## All endpoints

<CardGroup cols={2}>
  <Card title="Search and Ask" icon="magnifying-glass">
    [Search Judgments](/docs/api-reference/rest-apis/v2/memory/search) · [Ask Memory](/docs/api-reference/rest-apis/v2/memory/ask) · [Suggest Decision](/docs/api-reference/rest-apis/v2/memory/suggest) · [Query Judgments](/docs/api-reference/rest-apis/v2/memory/judgments/query)
  </Card>

  <Card title="Knowledge" icon="book">
    [Ingest](/docs/api-reference/rest-apis/v2/memory/knowledge/ingest) · [Ingest Status](/docs/api-reference/rest-apis/v2/memory/knowledge/ingest-status) · [Upload URL](/docs/api-reference/rest-apis/v2/memory/knowledge/upload-url) · [Search](/docs/api-reference/rest-apis/v2/memory/knowledge/search) · [List](/docs/api-reference/rest-apis/v2/memory/knowledge/list) · [Rules](/docs/api-reference/rest-apis/v2/memory/knowledge/rules) · [Update](/docs/api-reference/rest-apis/v2/memory/knowledge/update) · [Download](/docs/api-reference/rest-apis/v2/memory/knowledge/download) · [Delete](/docs/api-reference/rest-apis/v2/memory/knowledge/delete)
  </Card>

  <Card title="Insights" icon="chart-line">
    [Reviewer Profile](/docs/api-reference/rest-apis/v2/memory/profiles/get) · [Patterns](/docs/api-reference/rest-apis/v2/memory/patterns/get) · [Stats](/docs/api-reference/rest-apis/v2/memory/stats/get)
  </Card>

  <Card title="Alerts" icon="bell">
    [List](/docs/api-reference/rest-apis/v2/memory/alerts/list) · [Dismiss](/docs/api-reference/rest-apis/v2/memory/alerts/dismiss) · [Mark Actioned](/docs/api-reference/rest-apis/v2/memory/alerts/action) · [Get Config](/docs/api-reference/rest-apis/v2/memory/alerts/config/get) · [Update Config](/docs/api-reference/rest-apis/v2/memory/alerts/config/update)
  </Card>
</CardGroup>

<Tip>
  There are two searches, and they read different things. [Search Judgments](/docs/api-reference/rest-apis/v2/memory/search) reads past decisions. [Search Knowledge Base](/docs/api-reference/rest-apis/v2/memory/knowledge/search) reads the content of the files you ingested. Pass `includeRules: true` on the second one to search extracted rules too.
</Tip>

## Good to know

* **Judgments are read-only.** They come from your users' review activity, so a new workspace has none until people start reviewing. Comments from older SDK clients are enriched into Memory too, including a one-time backfill of historical data.
* **Ingestion is asynchronous.** `ingest` returns `processing`. Poll `ingest-status` until it is `completed` or `failed`.
* **Supported file types:** PDF, CSV, Excel (`.xlsx`), and plain text.
* **File limits:** 5 MB inline, 30 MB by reference.
* **Duplicate uploads** report the original source's status, so a duplicate can stay `processing` until the original finishes. `completed` always means safe to download.
* **Knowledge rate limits**, per API key per minute: `knowledge/ingest-status` 600, `knowledge/search` 120, `knowledge/upload-url` 100, `knowledge/ingest` 30, `knowledge/delete` 30. Going over returns `RESOURCE_EXHAUSTED`, so keep bulk imports and status polling under those ceilings.

## Errors

| Status                | Meaning                                                                                                                                                                          |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INVALID_ARGUMENT`    | The request failed validation, or the `x-velt-auth-token` header is missing.                                                                                                     |
| `PERMISSION_DENIED`   | The `x-velt-auth-token` is not valid for this API key.                                                                                                                           |
| `NOT_FOUND`           | Unknown `sourceId`, or an `x-velt-api-key` that does not resolve to a workspace. `knowledge/download` is the exception: an unknown `sourceId` there returns `null` with a `200`. |
| `ABORTED`             | Deleting a source that is still processing, or one that duplicate uploads point at. Poll `ingest-status` until the status is terminal, or delete the duplicates first.           |
| `FAILED_PRECONDITION` | The signed upload URL expired before you called `ingest`, or you called `knowledge/update` on a source that is not rule-based.                                                   |
| `RESOURCE_EXHAUSTED`  | Rate limited. Back off and retry.                                                                                                                                                |

On a validation error, every memory endpoint returns `details.issues` alongside `message` and `status`, listing each failing field.

The most common `INVALID_ARGUMENT` causes:

* `filters.annotationId` without `organizationId`.
* `documentId` without `organizationId` on `knowledge/ingest` or `knowledge/upload-url`.
* A `dateRange` whose `start` is after its `end`.
* An unsupported file type, or an inline file over 5 MB. Use [Get Upload URL](/docs/api-reference/rest-apis/v2/memory/knowledge/upload-url) for larger files.
* An unrecognized field on `knowledge/ingest`, `knowledge/upload-url`, `knowledge/ingest-status`, `knowledge/delete`, or `knowledge/search`. These five reject unknown keys, so a typo like `bas64` fails instead of being ignored. `knowledge/search` is workspace-wide and rejects `organizationId` and `documentId`.
