Auditable Call Memory for AI Phone Agents
AgentCall gives your AI phone agent cross-call memory it can recite, you can audit, and your customer can switch off. Every call produces a structured Call Report. Facts, commitments, and preferences are extracted with the caller's exact quote as evidence, stored against a contact, and loaded as a one-paragraph brief on the next inbound call.
What it does
Cross-call brief
On every inbound call, the AI loads a one-paragraph brief: who is calling, what was decided last time, what is outstanding, and what they prefer. The voice agent sounds like it actually remembers.
Source-backed
Every memory entry carries a sourceCallId and the exact quote the caller said. Open a brief, click any line, and jump to the transcript turn it came from.
Customer-controlled
Flip a single boolean to turn the whole system off. When off, nothing is extracted, no memory is read on inbound, no candidates are created. Default is on.
Quickstart
Memory is on by default for every account. Once your number has received its first call, you can fetch the brief that will load on the next call from the same number:
curl https://api.agentcall.co/v1/contacts/by-phone/+14155551234/next-call-context \
-H "Authorization: Bearer ac_live_xxxxxxxxxxxxx"Response:
{
"contactId": "ct_8a4f",
"brief": "Sarah at Acme Corp, enterprise prospect. Last call (May 15, 9 min): walked through pricing, agreed to a follow-up Wednesday 2 PM CT with three talking points: integration timeline, security review, and pilot scope. Sarah confirmed budget is approved. She prefers calls before noon Pacific.",
"updatedAt": "2026-05-15T19:14:08.000Z"
}If you have inbound AI configured with contextSource: agentcall_memory (the default when memory is on), the same brief is injected into the voice agent on every inbound call from this contact. No code to write.
The toggle
Auditable Call Memory is on by default. When off, no extraction job is enqueued after calls, no memory is read on inbound, and no candidates are created. Switch it from the dashboard overview, the SDK, or the MCP server.
Dashboard:
Go to agentcall.co/dashboard and toggle “Auditable Call Memory” in the overview card. Takes effect immediately.
Node SDK:
import { AgentCall } from 'agentcall'
const client = new AgentCall({ apiKey: process.env.AGENTCALL_API_KEY })
await client.account.setMemoryEnabled(true) // turn on
await client.account.setMemoryEnabled(false) // turn off
const account = await client.account.get()
console.log(account.memoryEnabled) // true | falseMCP (ask your agent):
“Turn off AgentCall memory on my account for now.”
Your agent calls set_memory_enabled with { enabled: false }.
REST:
curl -X PATCH https://api.agentcall.co/v1/account \
-H "Authorization: Bearer ac_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"memoryEnabled": false}'Endpoints
Every endpoint requires the standard Authorization: Bearer ac_live_xxx header. Examples below use realistic IDs so you can pattern-match your responses.
Read
/v1/contactsList every contact your number has talked to.
[
{ "id": "ct_8a4f", "phone": "+14155551234", "displayName": "Sarah at Acme", "tags": ["enterprise"], "verified": true, "lastCallAt": "2026-05-15T19:12:00.000Z" },
{ "id": "ct_a219", "phone": "+15125559921", "displayName": "Marcus Lee", "tags": [], "verified": false, "lastCallAt": "2026-05-14T16:03:00.000Z" }
]/v1/contacts/:idFull contact record with current memory entries and recent calls.
{
"id": "ct_8a4f",
"phone": "+14155551234",
"displayName": "Sarah at Acme",
"tags": ["enterprise"],
"ownerNotes": "Decision maker. Loves dry humor.",
"verified": true,
"memory": [ /* memory entries */ ],
"recentCalls": [ /* last 10 call summaries */ ]
}/v1/contacts/by-phone/:phoneLook up a contact by E.164 phone number.
{ "id": "ct_8a4f", "phone": "+14155551234", "displayName": "Sarah at Acme", "verified": true }/v1/contacts/:id/next-call-contextThe one-paragraph brief loaded into the AI on the next inbound call.
{
"contactId": "ct_8a4f",
"brief": "Sarah at Acme Corp, enterprise prospect. Last call (May 15, 9 min): walked through pricing, agreed to a follow-up Wednesday 2 PM CT with three talking points: integration timeline, security review, and pilot scope. Sarah confirmed budget is approved. She prefers calls before noon Pacific.",
"updatedAt": "2026-05-15T19:14:08.000Z"
}/v1/contacts/:id/memoryCurrent memory entries for a contact (facts, preferences, commitments, decisions).
[
{ "id": "mem_3c12", "kind": "fact", "text": "Sarah is VP Engineering at Acme Corp.", "sourceCallId": "call_29ab", "quote": "I lead engineering over at Acme.", "createdAt": "2026-05-11T15:20:00.000Z" },
{ "id": "mem_3c19", "kind": "preference", "text": "Prefers calls before noon Pacific.", "sourceCallId": "call_29ab", "quote": "Mornings work best, before lunch.", "createdAt": "2026-05-11T15:21:00.000Z" },
{ "id": "mem_4f02", "kind": "commitment", "text": "AgentCall to schedule Wed 2 PM CT follow-up with talking points.","sourceCallId": "call_44e1", "quote": "Let us do Wednesday at 2 your time.", "createdAt": "2026-05-15T19:12:00.000Z" }
]/v1/memory/:idA single memory entry.
{ "id": "mem_3c12", "kind": "fact", "text": "Sarah is VP Engineering at Acme Corp.", "sourceCallId": "call_29ab", "quote": "I lead engineering over at Acme.", "verified": true }/v1/memory/:id/timelineFull edit history for a memory entry. Every change keeps the prior text and who changed it.
[
{ "version": 1, "text": "Sarah is at Acme.", "editedBy": "extractor", "at": "2026-05-11T15:20:00.000Z" },
{ "version": 2, "text": "Sarah is VP Engineering at Acme Corp.", "editedBy": "owner", "at": "2026-05-12T10:02:00.000Z" }
]/v1/memory/candidatesPending extractor candidates waiting for owner approval. Use the approve / reject endpoints to promote or drop them.
[
{ "id": "cand_77b1", "contactId": "ct_8a4f", "kind": "fact", "text": "Acme has 250 employees.", "sourceCallId": "call_44e1", "quote": "We are at about 250 people now.", "confidence": 0.74 }
]/v1/calls/:id/reportStructured Call Report for a single call: summary, intent, urgency, entities, facts, decisions, commitments, tasks, preferences, unresolved, risks, nextAction, nextCallContext, ownerBrief.
{
"callId": "call_44e1",
"summary": "Sarah confirmed budget is approved. Set Wed 2 PM CT follow-up.",
"intent": "scheduling",
"urgency": "medium",
"entities": [{ "type": "person", "value": "Sarah" }, { "type": "org", "value": "Acme Corp" }],
"facts": [{ "text": "Budget is approved.", "quote": "Budget is signed off."}],
"decisions": ["Hold follow-up Wed 2 PM CT."],
"commitments": [{ "owner": "agentcall", "task": "Send three talking points by EOD Tuesday." }],
"tasks": ["Draft talking points: timeline, security, pilot scope."],
"preferences": ["Calls before noon Pacific."],
"unresolved": ["Confirm pilot start date."],
"risks": [],
"nextAction": "Email talking points and a calendar invite for Wed 2 PM CT.",
"nextCallContext": "Sarah at Acme, enterprise prospect, follow-up Wed 2 PM CT, budget approved.",
"ownerBrief": "Sarah locked Wednesday 2 PM CT. Send the three talking points before Tuesday EOD."
}/v1/call-reportsList recent Call Reports across all numbers. Filter by contactId or numberId.
[
{ "callId": "call_44e1", "contactId": "ct_8a4f", "intent": "scheduling", "urgency": "medium", "createdAt": "2026-05-15T19:14:08.000Z" },
{ "callId": "call_4391", "contactId": "ct_a219", "intent": "complaint", "urgency": "high", "createdAt": "2026-05-14T16:05:00.000Z" }
]/v1/briefsOwner brief inbox. Filter by status (open / resolved), urgency, or contactId. Briefs surface items that need human attention.
[
{ "id": "brief_91f2", "callId": "call_44e1", "contactId": "ct_8a4f", "urgency": "medium", "status": "open", "text": "Send Sarah talking points by Tuesday EOD." }
]Write
/v1/contacts/:idUpdate displayName, tags, ownerNotes, or verified. Owner edits override anything the extractor wrote.
// Request
{ "displayName": "Sarah Chen", "tags": ["enterprise", "champion"], "verified": true }
// Response
{ "id": "ct_8a4f", "displayName": "Sarah Chen", "tags": ["enterprise", "champion"], "verified": true }/v1/contacts/:idPurge a contact. Cascades to all memory entries, candidates, and briefs for that contact. Audit row is kept for compliance.
{ "purged": true, "contactId": "ct_8a4f", "memoryEntriesRemoved": 7, "candidatesRemoved": 2 }/v1/memory/:idEdit a memory entry's text or expiresAt. Old version is preserved in the timeline.
// Request
{ "text": "Sarah is SVP Engineering at Acme Corp (was VP, promoted Apr 2026).", "expiresAt": null }
// Response
{ "id": "mem_3c12", "version": 3, "text": "Sarah is SVP Engineering at Acme Corp (was VP, promoted Apr 2026).", "editedBy": "owner" }/v1/memory/:idDelete a memory entry. Timeline rows are kept; the active entry is removed from briefs.
{ "deleted": true, "id": "mem_3c12" }/v1/memory/candidates/:id/approvePromote a candidate into a permanent memory entry. Optionally edit the text before promoting.
// Request
{ "text": "Acme has ~250 employees (May 2026)." }
// Response
{ "promoted": true, "memoryId": "mem_5a02", "candidateId": "cand_77b1" }/v1/memory/candidates/:id/rejectDrop a candidate. It will not be re-suggested from the same source call.
{ "rejected": true, "candidateId": "cand_77b1" }/v1/briefs/:id/acknowledgeMark a brief as seen without resolving it. Useful for nightly digest workflows.
{ "id": "brief_91f2", "status": "acknowledged" }/v1/briefs/:id/resolveClose a brief. Optional note recorded on the audit row.
// Request
{ "note": "Talking points sent." }
// Response
{ "id": "brief_91f2", "status": "resolved" }Account
/v1/accountReturns account-level settings including memoryEnabled.
{ "plan": "pro", "memoryEnabled": true, "createdAt": "2026-04-25T14:02:00.000Z" }/v1/accountToggle memoryEnabled. Account-wide; applies to every number under the account.
// Request
{ "memoryEnabled": false }
// Response
{ "plan": "pro", "memoryEnabled": false }MCP tools
The hosted MCP server at api.agentcall.co/mcp ships 43 tools and 4 prompts total. 19 of those are the memory surface, plus 1 account toggle. Connect once from your AI client and ask it to read, audit, or correct memory in plain English.
Read (10)
list_contactsList every contact your number has talked to.get_contactFull contact record with memory and recent calls.get_contact_by_phoneLook up a contact by E.164 phone number.get_next_call_contextThe one-paragraph brief that loads on the next inbound call.get_current_memoryCurrent memory entries for a contact.get_memoryFetch a single memory entry by ID.get_memory_timelineFull edit history for a memory entry.list_memory_candidatesPending extractor candidates waiting for approval.list_call_reportsList recent Call Reports across your numbers.get_call_reportStructured Call Report for a single call.Write (9)
update_contactEdit displayName, tags, ownerNotes, verified.purge_contactDelete a contact and cascade memory entries.update_memoryEdit a memory entry's text or expiresAt.delete_memoryDelete a memory entry.approve_memory_candidatePromote a candidate into permanent memory.reject_memory_candidateDrop a candidate.list_briefsOpen owner-brief inbox, filterable by urgency.acknowledge_briefMark a brief as seen without resolving it.resolve_briefClose a brief with an optional note.Account (1)
set_memory_enabledTurn Auditable Call Memory on or off at the account level.Full setup for Claude Desktop, Cursor, Windsurf, VS Code, Hermes, OpenClaw, Codex, and OpenCode at /docs/mcp.
contextSource modes for inbound AI
When you call configure_inbound_ai (or PATCH a number), pick how the AI gets its pre-call context. Mix AgentCall's memory with your own bridge if you have both.
agentcall_memoryAgentCall memory (default when memory is on)Inject the cross-call brief built from prior Call Reports. Loads automatically. No infra to host.
webhookWebhook onlyCall out to your own context endpoint on every inbound call. Useful if your agent platform owns the source of truth.
mergeMergeConcatenate the AgentCall memory brief first, then your webhook content. Best of both: cross-call memory plus today's signals from your agent.
noneNoneSkip all context injection. The AI answers with only the system prompt you configured.
Running Hermes or another agent platform? See the Hermes integration guide for the merge-mode walkthrough where your daily brief and the AgentCall memory both load on the call.
The call.report.ready webhook
Subscribe to call.report.ready to receive the full structured Call Report as soon as extraction finishes. Fires a few seconds after call.transcript for the same call. Two events, two deliveries, signed and retried like every other AgentCall webhook.
{
"event": "call.report.ready",
"timestamp": "2026-05-15T19:14:11.234Z",
"data": {
"callId": "call_44e1",
"contactId": "ct_8a4f",
"report": {
"summary": "Sarah confirmed budget is approved. Set Wed 2 PM CT follow-up.",
"intent": "scheduling",
"urgency": "medium",
"entities": [{ "type": "person", "value": "Sarah" }, { "type": "org", "value": "Acme Corp" }],
"facts": [{ "text": "Budget is approved.", "quote": "Budget is signed off." }],
"decisions": ["Hold follow-up Wed 2 PM CT."],
"commitments": [{ "owner": "agentcall", "task": "Send three talking points by EOD Tuesday." }],
"tasks": ["Draft talking points: timeline, security, pilot scope."],
"preferences": ["Calls before noon Pacific."],
"unresolved": ["Confirm pilot start date."],
"risks": [],
"nextAction": "Email talking points and a calendar invite for Wed 2 PM CT.",
"nextCallContext": "Sarah at Acme, enterprise prospect, follow-up Wed 2 PM CT, budget approved.",
"ownerBrief": "Sarah locked Wednesday 2 PM CT. Send the three talking points before Tuesday EOD."
}
}
}Ordering: call.transcript fires first (right after the call ends). Extraction takes a few seconds, then call.report.ready fires with the structured fields above. If your agent already acted on the transcript, use callId to attach the Call Report to the same record. Full HMAC verification examples and retry semantics at /docs/post-call-webhook.
Auditability
Every memory entry comes with proof. Three guarantees the system ships with:
- sourceCallId. Every memory entry, candidate, and brief carries the ID of the call it came from. Jump to the transcript at any time.
- Exact quote. The evidence quote is preserved verbatim from the transcript turn. If the entry says “Sarah prefers calls before noon Pacific,” the quote is the line the caller actually said.
- Edit timeline. Every update to a memory entry, including the original extractor write, is kept in the timeline. Owner edits, extractor corrections, and deletions all carry an actor (
extractororowner) and a timestamp.
For a compliance review: pull GET /v1/contacts/:id/memory to see what the AI is using on calls today, then GET /v1/memory/:id/timeline on any specific entry to see who wrote and edited it. Calling the caller's right-to-delete is a single DELETE /v1/contacts/:id.
Keep reading
Give your AI agent a memory it can recite
Get an AgentCall API key, provision a number, and the first inbound call writes its first memory entry. Default on, off in one click.
Get API Key, Free