Agent Memory — API and MCP Reference
How AI agents read and write persistent memory in TrustPager — the 6 MCP tools, REST path, field reference, upsert mechanic, tags, linked entities, and a worked NurtureAgent example.
Agent Memory lets your AI agents store and retrieve structured knowledge between runs. An agent can write a memory after pitching a feature, and read it back before the next email to avoid repeating itself. Memory is scoped per-agent, per-subject — so the NurtureAgent remembers what it said to each contact independently.
For a general overview of AI agents in TrustPager, see https://trustpager.com/help-center/how-to-monitor-ai-agents-in-agent-hub.
What agent memory is
Each memory record has a few key fields:
- subject_type / subject_id — the CRM entity this memory is about (
contact,opportunity,company, etc.) and its UUID. - kind — the agent-defined category of memory. Every agent picks the kinds it will use. Common examples:
pitch,objection,preference,observation.kindis required on every write. - content — natural-language text, required. Write it as a sentence narrating to your future self: “Pitched automated follow-up sequences to Sarah. Angle: their team is stretched thin; sequences save 3 hours per week on manual follow-up.”
- metadata — optional structured JSONB payload. Use this for machine-readable fields you want to query or pass to downstream tools (e.g.
feature_pitched,pitch_angle,next_angles_to_try). - key — optional stable identifier within a
(agent, subject_type, subject_id, kind)tuple. Used for upsert — see below. - visibility —
"private"(default, only the writing agent can read) or"shared"(any agent in the workspace can read). - tags —
text[]of namespaced semantic facets such as["competitor:Twilio", "feature:voice_agents"]. Useful for cross-cutting facets that span subjects. Query vialist_memory(tag=...). - linked_entities —
[{type, id}]array for memories that reference multiple CRM entities. For example, a pitch memory is primarily about a contact but you can also link it to the opportunity it relates to. Query vialist_memory(linked_entity_type=opportunity, linked_entity_id=<uuid>). - source_kind / source_id — optional provenance fields linking the memory back to the event that triggered it (e.g.
source_kind: "email_send",source_id: "<email_log_id>").
API scopes
Your API key needs the following scopes:
memory:read— list, get, and search memoriesmemory:write— write, update, and delete memories
Manage scopes at https://app.trustpager.com/settings/api.
REST path
All memory endpoints are under /v1/memory.
GET /v1/memory— list and filter memories (subject_type,subject_id,kind,tag,linked_entity_type,linked_entity_id)GET /v1/memory/:id— get a single memory by idPOST /v1/memory— create or upsert a memoryPATCH /v1/memory/:id— partial update by idDELETE /v1/memory/:id— soft delete by idPOST /v1/memory/search— semantic search across memory content
MCP tools
All six memory operations are available as MCP tools:
list_memory— list and filter memories by subject, kind, tag, or linked entityget_memory— fetch a single memory by idwrite_memory— create a new memory, or upsert an existing one viakeyupdate_memory— partial update of an existing memory by iddelete_memory— soft delete a memory by idsearch_memory— semantic search across memory content and metadata
The upsert mechanic
Passing key on write_memory makes the call an upsert against the tuple (agent_registry_id, subject_type, subject_id, kind, key). Re-running the same tuple updates the existing memory row instead of creating a duplicate. Soft-deleted rows are restored and updated when upserted.
This is the natural way to enforce a never-twice gate. For example, a NurtureAgent pitching article slugs uses key: "<article_slug>". Pitching the same article a second time simply updates the existing row — no duplicate, no second pitch.
Worked example — NurtureAgent pitch tracking
A NurtureAgent wants to track that it pitched a feature to a contact, along with the angle used and next angles to try. The memory is contact-level because both the cooldown gate (“don’t email the same person twice in 3 days”) and the never-twice gate (“don’t pitch the same article twice”) are enforced per-contact. The related opportunity is linked via linked_entities.
write_memory(
agent_registry_id: "<your own id from kickoff>",
subject_type: "contact",
subject_id: "<contact_uuid>",
kind: "pitch",
key: "automated-follow-up-sequences",
content: "Pitched automated follow-up sequences to Sarah. Angle: their team is stretched thin; sequences save 3 hours per week on manual follow-up.",
metadata: {
feature_pitched: "Automated follow-up sequences",
pitch_angle: "saves 3 hours per week on manual follow-up",
stated_need_addressed: "team stretched thin on manual follow-up",
next_angles_to_try: [
"ROI calculator — show dollar value of time saved",
"Case study — similar business in their industry",
"Free trial offer — remove the risk of committing"
]
},
linked_entities: [
{ type: "opportunity", id: "<opportunity_uuid>" },
{ type: "email_log", id: "<email_log_id_from_send_response>" }
],
tags: ["feature:follow_up_sequences", "angle:time_savings"],
source_kind: "email_send",
source_id: "<email_log_id>"
)
On the next run, the agent calls list_memory(subject_type="contact", subject_id="<uuid>", kind="pitch") to see every pitch already made — and skips any article whose slug appears as a key in the results.
Lookup patterns
- By subject:
list_memory(subject_type="contact", subject_id="<uuid>")— all memories about a contact - By kind: add
kind="pitch"to narrow to pitch memories only - By tag:
list_memory(tag="feature:voice_agents")— cross-subject facet query - By linked entity:
list_memory(linked_entity_type="opportunity", linked_entity_id="<uuid>")— all memories that reference a given opportunity - Semantic search:
search_memory(query="stretched thin on follow-up")— vector search across content and metadata
Visibility
visibility: "private" is the default — only the agent that wrote the memory can read it. Set visibility: "shared" if you want other agents in the same workspace to read the memory. For example, a SupportAgent and a NurtureAgent might both need to know that a contact expressed a pricing objection.
Troubleshooting
- Write returns 409 / duplicate key: You are passing the same
(agent_registry_id, subject_type, subject_id, kind, key)tuple without intending to upsert. Either omitkey(creates a new row every time) or include it (upserts the existing row). - list_memory returns empty: Check that
subject_typeandsubject_idmatch exactly what was written. A contact UUID written undersubject_type: "contact"will not appear in a query forsubject_type: "opportunity". - Agent cannot read another agent’s memories: The writing agent used
visibility: "private"(the default). The writing agent needs to setvisibility: "shared"for cross-agent reads.