Save a conversation turn
curl --request POST \
--url https://api.tensorpro.ai/api/v1/memory/ingest \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"assistant_message": "Thanks, I will remember that.",
"session_id": "chat-42",
"user_message": "Sara manages the website project.",
"project_id": "<string>",
"timestamp": "2026-11-10T04:43:25Z"
}
'import requests
url = "https://api.tensorpro.ai/api/v1/memory/ingest"
payload = {
"assistant_message": "Thanks, I will remember that.",
"session_id": "chat-42",
"user_message": "Sara manages the website project.",
"project_id": "<string>",
"timestamp": "2026-11-10T04:43:25Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
assistant_message: 'Thanks, I will remember that.',
session_id: 'chat-42',
user_message: 'Sara manages the website project.',
project_id: '<string>',
timestamp: '2026-11-10T04:43:25Z'
})
};
fetch('https://api.tensorpro.ai/api/v1/memory/ingest', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tensorpro.ai/api/v1/memory/ingest",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'assistant_message' => 'Thanks, I will remember that.',
'session_id' => 'chat-42',
'user_message' => 'Sara manages the website project.',
'project_id' => '<string>',
'timestamp' => '2026-11-10T04:43:25Z'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tensorpro.ai/api/v1/memory/ingest"
payload := strings.NewReader("{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tensorpro.ai/api/v1/memory/ingest")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tensorpro.ai/api/v1/memory/ingest")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}"
response = http.request(request)
puts response.read_body{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": null
}
}
{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": "43466367-e4a1-4d68-b97a-793a1f6628a9"
}
}
{
"detail": "session_id: Field required",
"code": "VALIDATION_ERROR",
"errors": [{ "field": "session_id", "message": "Field required" }]
}
Save memory
Save a conversation turn
Save one turn of a conversation, the user’s message and your assistant’s reply, so EDN can remember what was said. Call it after every turn.
POST
/
api
/
v1
/
memory
/
ingest
Save a conversation turn
curl --request POST \
--url https://api.tensorpro.ai/api/v1/memory/ingest \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"assistant_message": "Thanks, I will remember that.",
"session_id": "chat-42",
"user_message": "Sara manages the website project.",
"project_id": "<string>",
"timestamp": "2026-11-10T04:43:25Z"
}
'import requests
url = "https://api.tensorpro.ai/api/v1/memory/ingest"
payload = {
"assistant_message": "Thanks, I will remember that.",
"session_id": "chat-42",
"user_message": "Sara manages the website project.",
"project_id": "<string>",
"timestamp": "2026-11-10T04:43:25Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
assistant_message: 'Thanks, I will remember that.',
session_id: 'chat-42',
user_message: 'Sara manages the website project.',
project_id: '<string>',
timestamp: '2026-11-10T04:43:25Z'
})
};
fetch('https://api.tensorpro.ai/api/v1/memory/ingest', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tensorpro.ai/api/v1/memory/ingest",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'assistant_message' => 'Thanks, I will remember that.',
'session_id' => 'chat-42',
'user_message' => 'Sara manages the website project.',
'project_id' => '<string>',
'timestamp' => '2026-11-10T04:43:25Z'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tensorpro.ai/api/v1/memory/ingest"
payload := strings.NewReader("{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tensorpro.ai/api/v1/memory/ingest")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tensorpro.ai/api/v1/memory/ingest")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"assistant_message\": \"Thanks, I will remember that.\",\n \"session_id\": \"chat-42\",\n \"user_message\": \"Sara manages the website project.\",\n \"project_id\": \"<string>\",\n \"timestamp\": \"2026-11-10T04:43:25Z\"\n}"
response = http.request(request)
puts response.read_body{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": null
}
}
{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": "43466367-e4a1-4d68-b97a-793a1f6628a9"
}
}
{
"detail": "session_id: Field required",
"code": "VALIDATION_ERROR",
"errors": [{ "field": "session_id", "message": "Field required" }]
}
Use this endpoint to save a conversation to EDN, one turn at a time. A turn is
one message from the user and your assistant’s reply to it.
Call it once after each reply. EDN saves the turn, and if the user said
something worth remembering, such as “Sara manages the website project”, EDN
remembers that fact for future conversations.
See Error handling.
What EDN does
- Saves the exchange as a
ROUNDmemory. - Checks the user’s message for a new fact or a correction.
- Saves a
USER_STATEDmemory when it finds one. If the message mixes a fact with a question, only the fact is saved. - If the message corrects an earlier fact, replaces that fact. See Important behavior for when this happens.
- Creates the session if the
session_idis new.
Required headers
| Header | Value | Description |
|---|---|---|
Authorization | Bearer YOUR_API_KEY | Your EDN API key. Create keys in the Console. See Authentication |
Content-Type | application/json | The request body is JSON |
Request
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Identifier for the conversation. Use the same value for every turn in the same conversation. If EDN hasn’t seen this value before, it creates a new session. Format: 1 to 128 characters, using only letters, digits, period (.), underscore (_), colon (:) and hyphen (-) |
user_message | string | Yes | The user’s message. EDN checks it for new facts and corrections. Must not be empty |
assistant_message | string | Yes | The assistant’s reply to user_message. Stored with the exchange, but EDN doesn’t take facts from it. Inline citation markers such as [1] are removed before storage. Must not be empty |
project_id | string | No | Identifier for the project to store the turn in. Omit to store the turn in general memory |
timestamp | string (date-time) | No | Date and time of the exchange, in ISO 8601 format, such as 2026-11-10T04:43:25Z. Use it when importing past conversations. If omitted, the turn is dated at the time of the request |
curl -X POST "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": "chat-42"
}'
import os
import requests
resp = requests.post(
"https://api.tensorpro.ai/api/v1/memory/ingest",
headers={"Authorization": f"Bearer {os.environ['EDN_API_KEY']}"},
json={
"user_message": "Sara manages the website project.",
"assistant_message": "Thanks, I will remember that.",
"session_id": "chat-42",
},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://api.tensorpro.ai/api/v1/memory/ingest", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.EDN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
user_message: "Sara manages the website project.",
assistant_message: "Thanks, I will remember that.",
session_id: "chat-42",
}),
});
console.log(await resp.json());
Response
200 OK
{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": null
}
}
| Field | Type | Description |
|---|---|---|
record_id | string | Identifier for the stored ROUND record: the saved exchange |
fact | object or null | The fact EDN stored from the user’s message, or null if it stored none |
fact.record_id | string | Identifier for the stored USER_STATED record |
fact.replaces | string or null | Identifier for the earlier record this fact replaced, when the user corrected it. Otherwise null |
Important behavior
-
fact: nullmeans EDN saved the conversation turn, but did not create a separate factual memory from the user’s message. This is normal for questions, small talk, or messages that do not contain a fact worth remembering. Fact detection never fails the request: if it can’t complete, the turn is still saved andfactisnull. -
fact.replacescontains the ID of an earlier fact when EDN detects that the user corrected it. The new fact is used in future SIPs, while the earlier version remains available in the record history. -
When a correction doesn’t replace anything. EDN replaces an earlier fact
only when it’s clear the new statement is about the same thing, and only if
the earlier fact came from the user (
USER_STATED) or from an AI answer (AI_INFERRED). Facts from documents, and facts the user confirmed, are never replaced from chat; EDN keeps both. In these casesfact.replacesisnull. - Store the transcript for better corrections. To recognise a correction, EDN compares the user’s message with the assistant’s previous reply stored in the session. If you also save messages with Add a message to a session, corrections are detected more reliably.
- Send each conversation turn once. EDN does not currently deduplicate repeated ingest requests, so sending the same turn twice creates duplicate records.
-
Importing older conversations: set
timestampto the time the conversation originally happened so EDN stores the correct date.
Errors
| Status | Code | When |
|---|---|---|
401 | UNAUTHORIZED | The API key is missing, invalid or revoked |
404 | NOT_FOUND | project_id isn’t one of your projects |
422 | VALIDATION_ERROR | A required field is missing or empty, session_id has invalid characters, or timestamp isn’t ISO 8601 |
429 | RATE_LIMITED | Per-minute or daily limit reached. Retry after Retry-After seconds |
500 | INTERNAL_ERROR | Saving failed on EDN’s side. Retry later |
{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": null
}
}
{
"record_id": "53c7483b-1238-4129-8dcf-411beea0473a",
"fact": {
"record_id": "8f0d2c61-5b7e-4a39-9c1e-2d4f6a8b0e17",
"replaces": "43466367-e4a1-4d68-b97a-793a1f6628a9"
}
}
{
"detail": "session_id: Field required",
"code": "VALIDATION_ERROR",
"errors": [{ "field": "session_id", "message": "Field required" }]
}
Authorizations
Body
application/json
Response
Successful Response
