# Endpoint reference

> Four endpoints today, and what is coming. Every field below is the wire name.

Product: Revo Memory API. Source: https://www.revo.ai/docs/memory-api/api-reference

### POST /ingest

Push one unit of content. Runs the full pipeline and queues per tenant, so content applies in the order it is sent.

| Field | Description |
| --- | --- |
| `content` (string, required) | The raw text. No shape is imposed. |
| `label` (string) | Human-readable descriptor for the kind of content. Defaults to "Ingested data point". |
| `id` (string) | Stable identifier for this source. Re-sending the same id replaces the stored point. Omit and one is generated. |
| `timestamp` (ISO 8601) | When the content happened, not when it was sent. Backdatable. Defaults to now. |
| `metadata` (object) | Arbitrary JSON stored alongside the point and never interpreted. |
| `append` (boolean) | Splice content onto the stored point instead of replacing it, for a growing log or a continuing conversation. Only the new tail is re-embedded. |
| `group` (string) | Free-form grouping key, max 200 characters. Points sharing a group are distilled into one dated record. |
| `teamId` (integer) | Required only when the token reaches more than one workspace. |
| `audience` (string or array, COMING SOON) | Scope this data point to particular end users instead of the whole workspace. See the roadmap for the date. |

```202accepted
{
  "teamId": 8837,
  "ingestionId": "b41c9e2a7f6d4c118a3e",
  "sourceId": "call-8821",
  "status": "queued"
}
```

### GET /ingest/{ingestionId}

Poll until the queue has applied the content and finished its deferred maintenance. Pass `teamId` as a query parameter if the token reaches more than one workspace.

```200ok
{
  "teamId": 8837,
  "ingestionId": "b41c9e2a7f6d4c118a3e",
  "status": "completed",
  "settled": true,
  "blocked": false,
  "readiness": {
    "raw": 1.0,
    "comprehension": 1.0,
    "consolidation": 1.0,
    "parked": 0
  }
}
```

- **Gate on `settled`, not on `status`.** `settled` is true only when every stage has drained with nothing parked. `status` can read `"completed"` while distillation is still outstanding, and reading then returns a half-written graph.
- `blocked: true` means every remaining row parked. Stop polling and inspect the feed rather than waiting out the timeout.
- `readiness` is a live progress vector, enough to show a real progress bar instead of a spinner.
- The queue is FIFO, so the **last** ingestion id of a feed covers the whole feed. There is no need to poll every id.

### POST /recall

Returns ranked evidence with its sources and no generated text, for running a separate model over the context.

| Field | Description |
| --- | --- |
| `query` (string, required) | A natural-language question. Retrieval plans its own traversals from it. |
| `maxTokens` (integer) | A token budget for the returned evidence rather than a result count. Evidence is included most-relevant-first until the budget is spent. Minimum 256. Omit for everything. |
| `limit` (integer) | Cap on evidence matches returned. |
| `queryTimestamp` (ISO 8601) | Anchors relative-time reasoning ("last week") to a point in the past. Use it when replaying a historical conversation. |
| `includeTimeline` (boolean) | Include the complete dated record for the subject, not only the top matches. |
| `verbatim` (boolean) | Return raw source excerpts instead of the distilled form. Ordering questions need this, because only the raw corpus preserves within-source order. |
| `tag` (string) | Free-form correlation tag, echoed into usage records. |
| `teamId` (integer) | Required only when the token reaches more than one workspace. |

```200ok
{
  "teamId": 8837,
  "results": [ /* typed matches, each with its source and score */ ],
  "evidence": [
    "Nadia confirmed the pilot ships March 14 (Account review call, 2026-02-03)",
    "Budget for the Acme pilot moved to 40k (Email from Nadia, 2026-07-28)"
  ],
  "truncated": false,
  "timelineIsVerbatim": false,
  "intent": "Lookup",
  "degradedArms": [],
  "usage": {
    "recallTokensIn": 812,
    "recallTokensOut": 96,
    "recallCostUsd": 0.000214
  }
}
```

- `degradedArms` names any retrieval arm that stayed degraded after its retry, which tells a partial search apart from a complete one that found little. An empty array means every arm ran.
- `truncated` means the token budget cut the result set. Both `results` and `evidence` are cut identically.

### POST /answer

Recall plus a grounded answer. Takes every `/recall` field, plus the three below.

| Field | Description |
| --- | --- |
| `query` (string, required) | The question to answer. |
| `mode` (pipeline \| agent) | `pipeline` (default) is one shot: retrieve, then answer. `agent` runs a loop that can refine its own retrieval before committing, at higher latency and cost. |
| `responseSchema` (JSON Schema) | Force a structured answer. Supported in `pipeline` mode only; sending it with `mode: "agent"` is rejected rather than silently ignored. |
| `budget` (string) | Named context budget for the answerer. |

```200ok
{
  "teamId": 8837,
  "answer": "March 14, 2026. Nadia confirmed it on the February 3 account review call.",
  "evidence": [
    "Nadia confirmed the pilot ships March 14 (Account review call, 2026-02-03)"
  ],
  "abstained": false,
  "answerer": "gemini-3.5-flash",
  "intent": "Lookup",
  "recordKept": 12,
  "recordTotal": 12,
  "degradedArms": [],
  "usage": {
    "answerTokensIn": 2140,
    "answerTokensOut": 38,
    "answerCachedTokensIn": 1792,
    "answerCostUsd": 0.000431,
    "recallTokensIn": 812,
    "recallTokensOut": 96,
    "recallCostUsd": 0.000214
  }
}
```

- `abstained: true` means the memory did not hold the answer, so the model declined to generate one. Handle it as a normal outcome.
- `recordKept` and `recordTotal` separate "the record never loaded" from "the record loaded and the answer still missed": a retrieval problem versus a reasoning one.
- In `agent` mode the response carries `toolCalls` instead of `evidence`, listing the retrieval steps the loop took.

### Entities, relationships and hops

> **Coming soon**
>
> Direct access to the graph the pipeline builds, rather than reaching it only through a question. Search and CRUD on entities, the memories behind one, the edges around it, and traversal out to a given depth.
>
> **None of these exist yet**, and the paths below are the intended shape rather than a contract. Everything they will expose is reachable today through `/recall`, which returns the same evidence with its sources. Tell us which of them an integration needs: that is what decides the order they ship in.

| Route | What it will do |
| --- | --- |
| `GET /entities` | Search entities by text and type. The list view over the graph, for populating a UI rather than answering a question. |
| `POST /entities` | Create an entity directly, instead of waiting for extraction to derive it from content. |
| `GET /entities/{id}` | One entity with its three layers: the mentions it was built from, the dated observations drawn out of them, and the value that holds now. |
| `PATCH /entities/{id}` | Correct or set values on an entity without re-ingesting the content it came from. |
| `DELETE /entities/{id}` | Remove an entity along with the facts derived from it. |
| `GET /entities/{id}/memories` | The memories behind one entity: verbatim mentions with their sources and timestamps, and the observations drawn from them. |
| `GET /entities/{id}/relationships` | The typed relationships into and out of an entity, with the dates they hold for. |
| `GET /entities/{id}/hops` | Traverse outward to a given depth, for walking a neighbourhood of the graph rather than querying it. |

### POST /configurations

> **Coming soon**
>
> Workspace configuration through the API: the custom ontology, the audiences a data point can be scoped to, and the defaults applied to ingestion. **This endpoint does not exist yet**, and the request shape is not settled, so do not build against the name.
>
> During early access we apply the same configuration by hand. Tell us what needs setting up when requesting access, or any time after.