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

# Caller memory schema

> Which caller facts an agent may remember, what the schema filters, and the memory changes read.

An agent can remember facts about the people who call it, so a returning caller is not asked the same questions twice. The memory schema says which facts the agent may keep. It is read from the agent's configuration; there is no route to write it.

| Route | Scope | Notes |
| - | - | - |
| `GET /v1/agents/{agent_id}/memory/schema` | `agents:read` | The agent's memory schema |
| `GET /v1/memory/changes` | `agents:read`, `owner` role | What the agents learned or corrected, newest first |

## Owner write API

All writes require `agents:write`, the owner-only `manage_contact_memory` capability, an active entitled workspace, and an agent visible to the credential. Existing workspace-bound storage and its database access rules apply; no new store is created.

* `POST /v1/memory`: `agent_id`, international `number` (for example `+12025550123`), `contact`, lowercase `key`, `value` (up to 300 characters), and `kind` (`fact` or `summary`, default `fact`). Returns 201 with the memory id. Facts use the existing replacement history. Summaries append entries.
* `PATCH /v1/memory/{memory_id}`: `agent_id` and `value`. Corrects a fact or summary by id, preserving correction history. Foreign rows return 404.
* `POST /v1/memory/reset`: `agent_id`, `number`, `reason`, `confirm: true`. Forgets all contacts on that one line for that agent, including verification credentials, and returns the existing deletion receipt. Legal hold returns 409.
* `PATCH /v1/agents/{agent_id}/memory`: boolean `enabled` and `expect_revision` from the agent draft read. Saves only the draft. Publish separately to make it live. A stale revision returns 409.

Writes use owner provenance, not caller-controlled verification claims. Missing workspace storage returns 503, never an invented successful memory write. The console offers fact and summary entry, correction, and confirmed line reset.

## The schema

```json theme={null}
{"object": "memory_schema", "enabled": true, "source": "settings",
 "entity_schemas": [{"entity_type": "contact",
                     "description": "Facts this agent may remember about a caller",
                     "identifier_field": "name",
                     "fields": [{"name": "preferred_time", "type": "string",
                                 "description": "Usual time of day", "required": false}]}]}
```

| `enabled` | `source` | `entity_schemas` |
| - | - | - |
| `false` | `null` | Empty. The agent remembers nothing |
| `true`, with fields named on the agent | `settings` | One `contact` entity holding those fields |
| `true`, with no fields named | `cached_version` | One `contact` entity holding four default fields: `preferred_time`, `preferred_language`, `name` and `first_name`, each text |

A field is `name`, `type` (`string` unless the agent says otherwise), `description` and `required`. An agent that names fields by name alone gets `string` fields with an empty description that are not required.

Memory is turned on with the `contact_memory` field of the agent's draft (`"on"`), and the fields are the `memory_schema` field, a list of names (`PATCH /v1/agents/{id}/draft`, [Agents](https://docs.sempera.org/agents.md)). The route reads the agent's configuration as last published, so a draft edit shows here only after the agent is published.

## What the schema decides

When a call ends, Sempera writes what it learned to the caller's memory. While memory is on, a fact it captured from the caller whose key is not a field of the schema is dropped. A caller's name is stored as `first_name`, so a schema that names its own fields should include `first_name` if names are to be remembered. A one-line summary of the call and the call's open tasks are not facts and are not filtered by the schema. Memory is kept for the retention period the workspace owner set.

The schema is a filter on writes only. It never removes what was stored before it changed, and a workspace owner can view, correct and forget a caller's memory in the App.

## Memory changes

`GET /v1/memory/changes` answers `{"data": {"changes", "initial_memory", "memory_history"}, "errors": null}`. It covers every agent the credential can read, or one agent with `agent_id`. `memory_history` is up to 100 entries, newest first, each with `id`, `timestamp`, `type` (`fact_update`, `summary_update` or `task_update`), `key`, `value`, `agent_id`, `source`, `verified`, `call_id`, `corrected_from` (the id of the entry this one replaced) and `superseded` (true once it was corrected; forgotten entries never appear). Add `call_id` (or `conversation_id`) to see one call's entries: `changes` is then what that call left in memory, as `facts` (key to value), `summary` and `open_items`, and a call with no entries is `404` with `{"data": null, "errors": [{"error": "NOT_FOUND", "message": "..."}]}`. Without `call_id`, `changes` is `null`. `initial_memory` is always `null`: Sempera keeps no snapshot of memory from before a call. The route needs the `owner` role as well as the scope; other roles are refused with `403 forbidden`.

## Contacts

Contacts are the people your agents have remembered, grouped across the workspace. These routes need the `owner` role as well as the scope.

| Route | Scope | Does |
| - | - | - |
| `GET /v1/contacts/schema` | `agents:read` | Reads the shared contact schema: `{fields, revision}`. |
| `PUT /v1/contacts/schema` | `agents:write` | Replaces the fields (up to 50, each `name`, `type` of `string`, `number`, `boolean` or `date`, `description`, `required`). Send the `revision` you read; a stale revision is `409 conflict`. |
| `POST /v1/contacts/merge` | `agents:write` | Groups two console profiles with `source_id` and `target_id`; answers `{id, merged, note}`. |
| `POST /v1/contacts/resolve` | `agents:read` | Finds a contact by `contact_id`, or by `phone` (international, starting with `+`) with an optional `name`. Answers `status` (`resolved`, `ambiguous` or `not_found`), the `contact` when exactly one matches, and the `candidates` as `{id, name}`. It never guesses between candidates. |

A merge groups profiles for the owner only. Each memory entry keeps its own agent, line, provenance, expiry and verification, nothing about how a caller is recognised on a call changes, and a caller never gets another person's history.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.