Import an exchange
POST/v1/memory
Hand Memtro a conversation it was not part of, and it learns from it as though it had been: no answer is generated.
Takes an exchange that happened somewhere else (a transcript you already hold, a conversation from another tool) and learns from it as though it had run through Memtro. No answer is generated; the only model call is the one that reads the transcript, charged to the organisation's background allowance and logged like any other. Afterwards the cards of up to four entities the exchange touched are refreshed, so a question about them minutes later reads the new facts.
The body is the chat-completions shape, with the last message an assistant turn, plus a memtro object with this endpoint's own options.
Example
Against https://platform.memtro.com, with your key in MEMTRO_API_KEY.
curl https://platform.memtro.com/v1/memory \
-H "Authorization: Bearer $MEMTRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Where did we land on the Baltic renewal?"},
{"role": "assistant", "content": "14 March, at £48,000, signed by Birthe."}
],
"memtro": {"occurred_at": "2026-03-02T09:30:00Z", "transcript": "exchange"}
}'
import os, requests
r = requests.post(
"https://platform.memtro.com/v1/memory",
headers={"Authorization": f"Bearer {os.environ['MEMTRO_API_KEY']}"},
json={
"messages": [
{"role": "user", "content": "Where did we land on the Baltic renewal?"},
{"role": "assistant", "content": "14 March, at £48,000, signed by Birthe."},
],
"memtro": {"occurred_at": "2026-03-02T09:30:00Z"},
},
)
r.raise_for_status()
print(r.json())
const res = await fetch("https://platform.memtro.com/v1/memory", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.MEMTRO_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
messages: [
{ role: "user", content: "Where did we land on the Baltic renewal?" },
{ role: "assistant", content: "14 March, at £48,000, signed by Birthe." },
],
memtro: { occurred_at: "2026-03-02T09:30:00Z" },
}),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
console.log(await res.json());
The response.
{
"object": "memory.ingest",
"learned": true,
"episode": "e4b0…",
"memory_ids": ["91d7…"],
"entity_ids": ["5a10…", "b7e2…"],
"cards": 2,
"memories": 1,
"personal": 0,
"entities": 2,
"relationships": 1,
"superseded": 0,
"corroborated": 0,
"attributes": 1,
"visibility": "org",
"usage": { "input_tokens": 2310, "output_tokens": 240, "total_tokens": 2550 }
}
Authentication
An API key, as Authorization: Bearer or x-api-key. Read-only keys are refused before the body is read. Writing into the organisation's shared memory needs the import permission; writing into your own does not.
- Every reply, errors included, carries
x-request-idandrequest-idheaders with the same id, startingreq-. Quote it to support. - Browsers may call it from any origin: it answers CORS preflights and allows the
Authorization,x-api-keyand Anthropic version headers.
Request body
JSON, with Content-Type: application/json.
messagesarray of objectsRequiredThe exchange, in the chat-completions message shape. The last message must be the assistant's, with text in it.
memtroobjectOptionalThis endpoint's options. (
offis not a memory setting here, and the chat options do not apply.)4 fields of
memtromemorystringOptionalWhere the facts go. The key's ceiling applies as in chat; a key with nowhere to write is refused with
memory_disabled.transcriptstringOptionalexchangereads the last turn for facts and the rest as context, as a chat turn does;conversationreads every turn. Useconversationfor a history nothing has read before.occurred_atstringOptionalWhen the exchange happened: an ISO date, or anything a JavaScript
Datecan read. Relative dates ("next Tuesday") are resolved against it, so set it for anything that did not happen today.learnstringOptionalalwaysoverrides the check that skips exchanges with nothing memorable in them.
Response
200application/json
What was learned, or why nothing was.
object"memory.ingest"OptionalAlways
memory.ingest.learnedbooleanOptionalWhether anything was stored.
reasonstringOptionalWhen nothing was learned: why.
episodestring or nullOptionalThe id of the recorded exchange.
memory_idsarray of stringsOptionalThe memories written.
entity_idsarray of stringsOptionalThe entities touched.
cardsintegerOptionalEntity cards refreshed.
memories, personal, entities, relationships, superseded, corroborated, attributesintegerOptionalCounts: memories stored, how many of them personal, entities, relationships, memories superseded, existing facts this exchange confirmed, and recorded values.
visibilitystringOptionalWhere it went:
org,personal, orteam:<id>for a team key.usageobjectOptionalinput_tokens,output_tokens,total_tokensspent reading it.
Errors
In the OpenAI envelope, {"error": {"message", "type", "param", "code"}}. What each code means and what to do about it is in the error catalogue.
| Status | Code | When |
|---|---|---|
| 400 | none | The body failed validation. |
| 400 | unsupported_parameter | The last message is not the assistant's or has no text ( |
| 400 | provider_not_configured | No model is available to read the exchange with. |
| 401 | invalid_api_key | The key is missing, malformed, revoked or expired. |
| 402 | subscription_inactive | The organisation has no active subscription. |
| 403 | read_only_key | The key was issued read only. |
| 403 | memory_disabled | The key may not write memory. |
| 403 | none | Writing into shared memory without the import permission: send |
| 429 | allowance_exhausted | The background allowance is spent. |
| 502 | upstream_error | Reading the exchange failed. |