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

# Agents memory console: browse, teach, and audit per-agent memory

> Use the Agents → Memory console to browse and filter every memory entry in your tenant, teach agents facts manually, audit one agent's corpus from its per-agent view, and do the same through the memory API.

# Agents memory console

Agents → Memory is the operator console for the long-term memory your AI agents accumulate. It covers the whole lifecycle of a memory entry: browsing what exists, adding a fact by hand, checking what a single agent has learned, and removing or erasing entries. Everything the console does is also available on the public API.

Customer memory, the per-contact store agents read and write during a conversation, is covered in [Customer memory](/guides/customer-memory). This page is about the console and per-agent surfaces built on top of it.

## Agent memory vs customer memory

Two scopes exist, and the console lets you move between them:

* **Customer memory** is namespaced by contact — the shared per-contact store every agent reads from, so a fact one agent captured is available to your other agents on the next turn.
* **Agent memory** is namespaced by agent — the slice of the same store one agent wrote or was taught. The per-agent view at `/agents/:id/memory` shows exactly one agent's corpus, so you answer "what does THIS agent know about this contact" without the rest of the tenant's entries mixed in.

Both scopes hold the same four entry types — `fact`, `preference`, `goal`, `summary` — and both respect the contact's memory consent flag.

## The Agents → Memory console

Open **AI Agents → Memory**. The page lists memory entries for your tenant as cards showing content, type, and importance, including a filter field for Agent ID (`agt_…`) alongside Contact ID (`cnt_…`), type, and a creation-date range. Enter an agent ID to narrow the grid to that agent's entries; combine it with a contact ID to see what one agent knows about one contact.

Entries load 50 at a time; a **Load more** button extends the list when a cursor says more exist. Click a card for the full entry — the complete content, contact and agent IDs, importance, creation time, and the entry ID. Filter inputs are debounced, so pasting an ID issues one fetch, and an inverted From/To range is flagged inline rather than silently returning an empty list.

Access to this page is restricted to **owner and admin** roles, because it can surface notes across every contact and agent in the tenant.

## Teach your agent a fact

The **Create memory** button in the header opens the teach dialog. Its fields map 1:1 to the create-memory API payload:

1. **Contact ID** — the contact (`cnt_…`) this fact is about. Paste it from a conversation or contact page.
2. **Agent** — pick from a dropdown of your agents, so no one has to copy an `agt_…` id out of a URL.
3. **Type** — `fact`, `preference`, `goal`, or `summary`.
4. **Content** — a short, single-sentence statement, up to 10,240 characters.

Manual entries land at an importance of 0.9 by default, so an operator-authored fact outlives auto-extracted summaries under budget eviction. The agent retrieves a taught fact on its next conversation with that contact, exactly like a fact the runtime extracted — manual add does not skip retrieval ranking, and every entry still passes through the same grounding and guardrail pipeline described in [Agent grounding and citations](/guides/agent-grounding-citations).

## Per-agent view

On an agent's detail page, open **Memory**. The page at `/agents/:id/memory` shows what that one agent has remembered about a given contact, grouped by contact, so you can audit a single agent's corpus.

Paste a contact ID and press **Load**; the page fetches facts only by default, with controls to **Reveal PII** (owner, admin, or developer roles; the reveal is audit-logged) and to **Teach a fact** scoped to that agent and contact. Search filters the loaded facts inline without re-querying. If the contact has opted out of memory, the page says so explicitly instead of listing entries — and once you type a different contact ID, the stale opt-out warning is suppressed until you load it.

Each fact carries a **Forget** action (removes that entry, audit-logged), and each contact group carries **Forget all** — the same GDPR erasure as the console's erase panel: it deletes the contact's memory entries across all agents and flips the contact's memory consent flag off.

Erasure is the only way memory leaves an agent's corpus automatically. If you want an entry gone from one agent but retained for others, remember that the store is per-contact and shared — forgetting it removes it for every agent.

## API surface

Writes require owner or admin; requests authenticate with your session token or API key. The paths below are the same ones the console calls.

| Operation                                           | Endpoint                                       |
| --------------------------------------------------- | ---------------------------------------------- |
| List entries (filter by contact, agent, type, date) | `GET /api/v1/agents/memory`                    |
| Fetch a single entry                                | `GET /api/v1/agents/memory/:id`                |
| Create a manual entry                               | `POST /api/v1/agents/memory`                   |
| Promote (raise importance)                          | `PATCH /api/v1/agents/memory/:id`              |
| Delete a single entry                               | `DELETE /api/v1/agents/memory/:id`             |
| Erase a contact's memory                            | `DELETE /api/v1/agents/memory?contactId=cnt_…` |

Every response wraps its payload in a `{ data, meta }` envelope — `meta.request_id` and `meta.timestamp` — and every error is `{ error: { code, message, status }, meta }` with a stable machine `error.code` you can switch on. The samples below cover the three operations the console performs most often.

### List entries — `GET /agents/memory`

cURL:

```bash theme={null}
curl -sG 'https://api.orbit.devotel.io/api/v1/agents/memory' \
  -H 'Authorization: Bearer '$(get_jwt) \
  --data-urlencode 'agentId=agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2' \
  --data-urlencode 'memoryType=fact' \
  --data-urlencode 'limit=50'
```

The payload carries `items`, an opaque `next_cursor` to pass back as `?cursor=…` for the next page, a `total_scanned` count of entries matching the active filter, and an echo of the applied filter:

```json theme={null}
{
  "data": {
    "items": [
      {
        "id": "mem_9f2c1a8b4d6e4f0a9c2b5d7e3f1a6c84",
        "tenantId": "tn_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
        "contactId": "cnt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
        "agentId": "agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
        "memoryType": "fact",
        "content": "Customer prefers SMS over email",
        "importance": 0.9,
        "createdAt": "2026-09-10T14:22:31.120Z"
      }
    ],
    "next_cursor": null,
    "total_scanned": 1,
    "filter": {
      "contact_id": null,
      "agent_id": "agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "memory_type": "fact",
      "from": null,
      "to": null
    }
  },
  "meta": { "request_id": "req_01J8", "timestamp": "2026-09-10T14:22:31.201Z" }
}
```

TypeScript (Node SDK — the typed resources do not expose a dedicated memory accessor yet, so use the generic `request()` escape hatch, which still handles auth, retries, and idempotency keys):

```typescript theme={null}
import { Orbit } from '@devotel-orbit/node';

const orbit = new Orbit({ apiKey: 'dv_live_sk_xxxx' });

const res = await orbit.request('GET',
  '/agents/memory?agentId=agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2&memoryType=fact&limit=50');
for (const item of res.data.items) {
  console.log(item.memoryType, item.content);
  // carry res.data.next_cursor into the next call's ?cursor=… to page
}
```

### Fetch one entry — `GET /agents/memory/:id`

Returns the same entry shape under `data.item`. An unknown or cross-tenant id is a deliberate 404, never a leak.

```bash theme={null}
curl -s 'https://api.orbit.devotel.io/api/v1/agents/memory/mem_9f2c1a8b4d6e4f0a9c2b5d7e3f1a6c84' \
  -H 'Authorization: Bearer '$(get_jwt)
```

```json theme={null}
{
  "data": {
    "item": {
      "id": "mem_9f2c1a8b4d6e4f0a9c2b5d7e3f1a6c84",
      "tenantId": "tn_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "contactId": "cnt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "agentId": "agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "memoryType": "fact",
      "content": "Customer prefers SMS over email",
      "importance": 0.9,
      "createdAt": "2026-09-10T14:22:31.120Z"
    }
  },
  "meta": { "request_id": "req_01J8", "timestamp": "2026-09-10T14:22:31.201Z" }
}
```

TypeScript:

```typescript theme={null}
const res = await orbit.request('GET',
  '/agents/memory/mem_9f2c1a8b4d6e4f0a9c2b5d7e3f1a6c84');
console.log(res.data.item.content);
```

### Create a manual entry — `POST /agents/memory`

The same payload the teach dialog sends. `memoryType` defaults to `fact`, and `importance` is optional — manual entries default to 0.9.

```bash theme={null}
curl -X POST 'https://api.orbit.devotel.io/api/v1/agents/memory' \
  -H 'Authorization: Bearer '$(get_jwt) \
  -H 'Content-Type: application/json' \
  -d '{
    "contactId": "cnt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
    "agentId": "agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
    "memoryType": "fact",
    "content": "Customer prefers SMS over email"
  }'
```

The create returns `201 Created` with the stored entry:

```json theme={null}
{
  "data": {
    "item": {
      "id": "mem_9f2c1a8b4d6e4f0a9c2b5d7e3f1a6c84",
      "tenantId": "tn_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "contactId": "cnt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "agentId": "agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2",
      "memoryType": "fact",
      "content": "Customer prefers SMS over email",
      "importance": 0.9,
      "createdAt": "2026-09-10T14:22:31.120Z"
    }
  },
  "meta": { "request_id": "req_01J8", "timestamp": "2026-09-10T14:22:31.201Z" }
}
```

TypeScript:

```typescript theme={null}
const res = await orbit.request('POST', '/agents/memory', {
  contactId: 'cnt_01J8Z9K3P4Q5R6S7T8U9V0W1X2',
  agentId: 'agt_01J8Z9K3P4Q5R6S7T8U9V0W1X2',
  memoryType: 'fact',
  content: 'Customer prefers SMS over email',
});
console.log('Stored memory', res.data.item.id);
```

### Error envelope

Errors share one shape: `error.code` is a stable machine string, `error.details` carries per-field violations on validation failures. A `422` means the request body or query failed validation — fix the named field; a `409` with `MEMORY_FACTS_CAP_EXCEEDED` means the contact is at its facts cap — forget an existing fact before teaching another; an invalid filter shape on the list endpoint is also a 422.

A failing create:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid body",
    "status": 422,
    "details": {
      "memoryType": ["Invalid enum value. Expected 'summary' | 'fact' | 'preference' | 'goal'"]
    }
  },
  "meta": { "request_id": "req_01J8", "timestamp": "2026-09-10T14:22:31.201Z" }
}
```

And the fact-cap rejection:

```json theme={null}
{
  "error": {
    "code": "MEMORY_FACTS_CAP_EXCEEDED",
    "message": "This contact already has 50 facts (cap: 50). Forget one before teaching another.",
    "status": 409,
    "details": { "current": 50, "cap": 50 }
  },
  "meta": { "request_id": "req_01J8", "timestamp": "2026-09-10T14:22:31.201Z" }
}
```

For a fuller treatment of auth schemes and the envelope every endpoint returns, see [API integration](/guides/api-integration) and [Error handling examples](/guides/error-handling-examples).

## Limits and responsibilities

* Manual add is an explicit, audit-logged admin action — it never happens implicitly from a conversation.
* A contact can opt out of memory entirely; the per-contact erasure deletes existing entries and stops new writes until the flag is re-enabled on the contact's profile.
* Memory feeds agent grounding and recall — it does not bypass the guardrail stack. Retrieval still ranks entries, and grounded responses still cite their sources where citations are enabled (see [Agent grounding and citations](/guides/agent-grounding-citations)).
* There is a hard cap on `fact` entries per contact; when it is reached the API rejects new facts with a `409 MEMORY_FACTS_CAP_EXCEEDED` until an existing fact is forgotten.

## Next steps

* [Customer memory](/guides/customer-memory) — the per-contact store and its runtime behaviour.
* [Agent grounding and citations](/guides/agent-grounding-citations) — how retrieved memory and knowledge ground responses.
* [Agent guardrails](/agents/guardrail-effectiveness) — the controls memory retrieval still passes through.
