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

# Writing memory

> Save each conversation turn to EDN, and optionally keep track of what your AI said.

After each turn of a conversation, your app tells EDN what was said. EDN
decides what to remember and labels every record with its source.

## Which calls to make

| Call | When | Required? | What EDN saves |
| - | - | - | - |
| `memory/ingest` | After every turn | **Yes** | The exchange, any facts the user stated, and any corrections |
| `memory/infer` | After `memory/ingest` | Optional | The AI's answer, if it answers a factual question and passes EDN's check |
| `memory/generated` | Only when `memory/infer` returns `AI_GENERATED` | Optional | The AI's statement, kept separate as unconfirmed |

For documents, see the guide *Grounding answers in documents*. Corrections are
handled by `memory/ingest`; see [Corrections](https://docs.tensorpro.ai/concepts/corrections.md).

The examples use the `EDN_API_KEY` and `EDN_SESSION_ID` environment variables
from the [Quickstart](https://docs.tensorpro.ai/quickstart.md#1-create-an-api-key).

## Save each turn

Call `memory/ingest` with the user's message and your model's reply:

```bash cURL theme={"theme":"github-light"}
curl https://api.tensorpro.ai/api/v1/memory/ingest \
  -H "Authorization: Bearer $EDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_message": "Sara manages the website project.",
    "assistant_message": "Thanks, I will remember that.",
    "session_id": "'"$EDN_SESSION_ID"'"
  }'
```

```python Python theme={"theme":"github-light"}
import os
import requests

EDN = "https://api.tensorpro.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['EDN_API_KEY']}"}
session_id = os.environ["EDN_SESSION_ID"]

resp = requests.post(
    f"{EDN}/memory/ingest",
    headers=HEADERS,
    json={
        "user_message": "Sara manages the website project.",
        "assistant_message": "Thanks, I will remember that.",
        "session_id": session_id,
    },
)
saved = resp.json()
```

```typescript TypeScript theme={"theme":"github-light"}
const EDN = "https://api.tensorpro.ai/api/v1";
const HEADERS = {
  Authorization: `Bearer ${process.env.EDN_API_KEY}`,
  "Content-Type": "application/json",
};
const sessionId = process.env.EDN_SESSION_ID;

const resp = await fetch(`${EDN}/memory/ingest`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    user_message: "Sara manages the website project.",
    assistant_message: "Thanks, I will remember that.",
    session_id: sessionId,
  }),
});
const saved = await resp.json();
```

| Field | Required | What it does |
| - | - | - |
| `user_message` | Yes | What the user said |
| `assistant_message` | Yes | Your model's reply. Citation markers like `[1]` are removed before saving |
| `session_id` | Yes | The conversation this turn belongs to |
| `project_id` | No | Save into a project instead of general memory |
| `timestamp` | No | When the exchange happened (ISO 8601), for importing past conversations. Omit it to use the current time |

```json Response theme={"theme":"github-light"}
{
  "record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
  "fact": {
    "record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
    "replaces": null
  }
}
```

| Field | What it means |
| - | - |
| `record_id` | The ID of the saved conversation turn (a `ROUND` record) |
| `fact` | The fact EDN saved from the user's message, as a `USER_STATED` record. `null` when the message didn't state a fact |
| `fact.replaces` | The ID of the older fact this one replaced, when the user corrected something. Otherwise `null`. See [Corrections](https://docs.tensorpro.ai/concepts/corrections.md) |

## Keep track of AI answers (optional)

Your model's answers are never used as context on their own. But if you record
them, a user can later **confirm** one, and it becomes a trusted fact. Two
calls do this.

### 1. Classify the reply with `memory/infer`

Pass the `sip` you got from `sip/build` earlier in the turn.

```bash cURL theme={"theme":"github-light"}
curl https://api.tensorpro.ai/api/v1/memory/infer \
  -H "Authorization: Bearer $EDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_message": "Who manages the website project?",
    "assistant_message": "Sara manages the website project.",
    "session_id": "'"$EDN_SESSION_ID"'",
    "sip": "PASTE_THE_SIP_HERE"
  }'
```

```python Python theme={"theme":"github-light"}
resp = requests.post(
    f"{EDN}/memory/infer",
    headers=HEADERS,
    json={
        "user_message": user_text,
        "assistant_message": reply,
        "session_id": session_id,
        "sip": sip["sip"],  # from sip/build
    },
)
result = resp.json()
```

```typescript TypeScript theme={"theme":"github-light"}
const inferResp = await fetch(`${EDN}/memory/infer`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    user_message: userText,
    assistant_message: reply,
    session_id: sessionId,
    sip: sip.sip, // from sip/build
  }),
});
const result = await inferResp.json();
```

```json Response theme={"theme":"github-light"}
{
  "saved": true,
  "provenance": "AI_INFERRED",
  "record_id": "c2e9a7d4-3f1b-4e8a-b6d0-91a5f3c7e284",
  "reason": null
}
```

* If the reply answers the user's factual question and passes EDN's check, EDN
  saves it as `AI_INFERRED`: `saved` is `true` and `record_id` is set.
* Pass the `sip` so EDN can check the reply against the memory the model was
  given. Without it, replies that state specific figures, or say who holds a
  role, are not saved.
* When nothing is saved, `saved` is `false` and `reason` says why:

| `reason` | Meaning |
| - | - |
| `not_an_answer` | The reply doesn't answer a factual question from the user. `provenance` is `AI_GENERATED` |
| `abstention` | The reply says it doesn't know |
| `unsupported_claim` | A fact in the reply isn't backed by the `sip` you sent |

### 2. If the result is `AI_GENERATED`, save it with `memory/generated`

```bash cURL theme={"theme":"github-light"}
curl https://api.tensorpro.ai/api/v1/memory/generated \
  -H "Authorization: Bearer $EDN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"assistant_message": "Here is a draft plan for the launch.", "session_id": "'"$EDN_SESSION_ID"'"}'
```

```python Python theme={"theme":"github-light"}
if result["provenance"] == "AI_GENERATED":
    requests.post(
        f"{EDN}/memory/generated",
        headers=HEADERS,
        json={"assistant_message": reply, "session_id": session_id},
    )
```

```typescript TypeScript theme={"theme":"github-light"}
if (result.provenance === "AI_GENERATED") {
  await fetch(`${EDN}/memory/generated`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ assistant_message: reply, session_id: sessionId }),
  });
}
```

```json Response theme={"theme":"github-light"}
{ "record_id": "5d81f0b3-9a6c-4e27-8b4f-0c3e2a7d1f96" }
```

Neither `AI_INFERRED` nor `AI_GENERATED` records are used as context until a
user confirms them with `memory/confirm`.

## Importing past conversations

To load conversation history, call `memory/ingest` once per past turn, with
`timestamp` set to when it happened. EDN dates each record by that time instead
of the moment you import it.

## Next steps

> [card] [Building context](https://docs.tensorpro.ai/guides/building-context.md)
>
> Read memory back with a SIP.

> [card] [Corrections](https://docs.tensorpro.ai/concepts/corrections.md)
>
> How changed facts are handled.

> [card] [Memory records](https://docs.tensorpro.ai/concepts/memory-records.md)
>
> What these calls create.


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