Truememory automatically learns from conversations, but sometimes you need to feed it information from outside the chat — a CRM profile, user preferences from onboarding, health metrics, or notes from another system. The Graph API lets you push arbitrary data into a user’s knowledge graph so the LLM can reference it in future conversations.
POST https://your-tie-host/memories/graph
Authorization: Bearer $TOKEN
Content-Type: application/jsonRequest body:
{
"data": "User is a senior engineer at Mindvalley. Prefers dark mode. Timezone is GMT+8.",
"type": "text",
"agent_id": "chatbot",
"source_description": "Onboarding profile import",
"created_at": "2026-04-01T00:00:00Z"
}| Field | Type | Required | Description |
|---|---|---|---|
data |
string | Yes | The content to ingest (max 50,000 characters) |
type |
"text" | "json" | "message" | "document" |
Yes | Shape of the data (see below). Selects the extraction rules and nothing else. |
agent_id |
string | One of | Targets the calling user’s memory, determining its scope. Provide exactly one of agent_id or graph_id. |
graph_id |
string | One of | Targets a graph namespace instead of user memory. Service token required. |
source_description |
string | No | Label describing where the data came from (max 500 characters). For per-record ingests this is the record’s identity stamp — see Ingesting Records |
created_at |
ISO 8601 datetime | No | When the data was originally created (defaults to now) |
wait |
boolean | No | When true, the request blocks until ingestion completes and returns 200 with {"status": "completed"} instead of 202. Useful for tests and small writes; expect multi-second latency. |
Data types:
| Type | Use for | Example |
|---|---|---|
text |
Free-form statements, profile descriptions, paragraphs | "User enjoys hiking and lives in Kuala Lumpur" |
json |
Structured records from APIs or databases | "{\"name\": \"Alice\", \"role\": \"engineer\"}" |
message |
Conversation-style exchanges | "user: I love dark mode\nassistant: Noted!" |
document |
A note or document the user brought in whole — a journal entry, an article, an upload | "Read Atomic Habits this week. The two-minute rule is the part I want to keep." |
note is accepted as the older name for document and behaves identically. Whatever the type, the graph is read back the same way: the type never decides what the visualization shows.
Response (202 Accepted):
{
"status": "processing"
}The response returns immediately. Entity extraction and relationship building happen in the background — processing time varies depending on data size and system load. After processing completes, the new data will appear in cache-warm results and timeline responses.
Formatting Data for Extraction
Truememory uses Graphiti to extract entities, relationships, and facts from the text you send. The quality of extraction depends heavily on how you format the data. Graphiti needs declarative statements — not questions, single words, or ambiguous fragments.
Write facts, not questions. Graphiti extracts meaning from factual statements. A question like “What do you want to do?” contains no information to store.
| Format | Extractable? | Why |
|---|---|---|
"User wants to focus on improving their health" |
Yes | Clear entity (user) + fact (goal is health) |
"What's something you want to pull off? — Life" |
No | A question with a one-word answer — no extractable relationship |
"User's goal for the next few months is related to Life" |
Marginal | Better, but “Life” is too vague to create a useful entity |
For onboarding or survey data, rephrase question-answer pairs into declarative statements on the client side before sending:
{
"data": "The user's top priority for the next few months is improving their fitness and running a half marathon.",
"type": "text",
"agent_id": "chatbot",
"source_description": "Onboarding survey response"
}If the user’s answer is too vague to form a useful statement (e.g. a single word like “Life”), consider either skipping ingestion or wrapping it with the question for context:
{
"data": "When asked about their goals for the next few months, the user answered: Life.",
"type": "text",
"agent_id": "chatbot"
}For conversation-style data, use the message type with role prefixes. This is the same format Truememory uses internally when flushing conversation history to memory:
{
"data": "user: I've been meditating every morning for the past month\nassistant: That's great progress! How long are your sessions?",
"type": "message",
"agent_id": "chatbot"
}Ingesting Records
For structured records from APIs or databases, use the json type. Extraction parses the structure and creates a fact for every field, including list fields (one fact per item) and identifier fields:
{
"data": "{\"name\": \"Alice Chen\", \"role\": \"Senior Engineer\", \"team\": \"Platform\", \"timezone\": \"GMT+8\"}",
"type": "json",
"agent_id": "chatbot",
"source_description": "CRM profile sync"
}Three conventions make record ingestion reliable. They matter most for namespace records that describe many different people.
Stamp every record, and embed the record’s subject id in the stamp. Give each record its own source_description (acme-member-profile:<subjectId>) and re-use the exact stamp when you re-send the record as a correction. The stamp is not just a label: entity resolution reads the subject id out of it to find the record’s existing node on a correction — even after a display-name change — and to keep two different subjects with the same name on two separate nodes. One constant stamp for a whole import collapses every record into one subject.
Put the subject’s stable id in the document. An id field on the record (for example tieUserId) lands on the subject’s node as external_id, giving you a deterministic lookup key. Field values that equal another node’s external_id become real edges automatically — a hostTieUserId on an event record produces a HOST edge to that member’s node.
Say who the document is about when the subject is not the app user. Extraction treats a profile-shaped document as the speaking user by default. When the record describes a third party (a member of a community, a CRM contact), state that inside the document itself — the key names are yours, only the content matters:
{
"data": "{\"documentKind\": \"third_party_member_profile\", \"subjectNote\": \"This document describes a member of the community. The member is not the speaker and not the app user.\", \"tieUserId\": \"0f4b9c21-...\", \"name\": \"Dana Ferreira\", \"city\": \"Kuala Lumpur\"}",
"type": "json",
"graph_id": "acme__members",
"source_description": "acme-member-profile:0f4b9c21-..."
}Without this framing the subject is labelled User — a protected, singleton label that cannot be deleted or made public. With it, the subject becomes a normal Person node carrying external_id.
Corrections are re-writes. To update or cancel a record, send the full document again with the same stamp. The subject resolves to its existing node, changed facts supersede the old ones (the old fact is marked invalid, not deleted), and unchanged facts are not duplicated.