Voice agent integration · 15 minutes quickstart · Inbound Call Webhook (webhook_url)
Mnemix + Retell AI
Retell AI's core is a Custom LLM WebSocket, but you don't need to take over that socket to add memory. Mnemix hooks into Retell's lighter "Inbound Call Webhook" (the `call_inbound` event) to resolve the caller before the call connects, returning recall_and_enrich results as `retell_llm_dynamic_variables` the agent's prompt can reference by `{{name}}`. When the call ends, Retell's `call_analyzed` webhook fires back the structured transcript and analysis, which Mnemix writes to the caller's profile for next-call recall — for both inbound and outbound calls.
- What
- Mnemix is the memory + real-world enrichment layer for AI agents — voice-first for phone callers.
- Who
- For developers building AI voice agents on Vapi, Retell, Bland, LiveKit, or Twilio.
- Price
- Hobby $0 (free tier). Starter, Pro, and Elite tiers — contact sales for pricing while billing is in private beta.
- How
- For cold voice callers, call POST /v1/recall_and_enrich before the first turn; it creates the contact on miss and starts Trestle and Twilio enrichment. Use POST /v1/calls/end for post-call write-back and GET /v1/caller/{phone_number} for read-only caller profiles. Designed for sub-300ms voice recall at the Cloudflare edge.
What Mnemix adds to Retell AI
- Caller resolved before the call connects Retell's Inbound Call Webhook (call_inbound) fires before the call / SMS object even exists — Mnemix runs recall_and_enrich against from_number in that window and returns the result as retell_llm_dynamic_variables, so the agent's first line already knows the caller.
- Structured write-back on call_analyzed, not a raw transcript dump Retell's call_analyzed event ships transcript_object (role/content/word-timestamps) and a call_analysis block (summary, sentiment, call_successful). Mnemix normalizes both into one v1/calls/end write so the next recall has a clean session record, not just a string blob.
- One integration, both call directions Retell's webhook payload is identical for inbound and outbound calls — only the direction field differs — so the same write-back handler covers agent-initiated outbound campaigns and inbound support lines without duplicate logic.
Code sample
SDK form with @mnemix-ai/client.
import { Mnemix } from "@mnemix-ai/client";
const mx = new Mnemix({ apiKey: process.env.MNEMIX_API_KEY! });
// Retell POSTs both event types to the same webhook_url — branch on
// payload.event, the same one-URL pattern most platforms here use.
// Set this URL as the agent's `webhook_url` (per-agent) or in the
// account-level Webhooks tab of the Retell dashboard.
export async function POST(req: Request) {
const payload = await req.json();
// ── PRE-CALL: Retell's "Inbound Call Webhook" ────────────────────────────
// Fires BEFORE the call is answered. Retell's own docs note there is no
// call_id yet at this point ("you will not have a call / SMS object and
// call / SMS id inside the payload"), so from_number is the only stable
// key. This is the lighter path — no Custom LLM WebSocket takeover needed.
if (payload.event === "call_inbound") {
const { from_number } = payload.call_inbound;
const { caller, memory, enrichment } = await mx.recall_and_enrich({
phone_number: from_number,
trigger: "ringing",
});
// Verified constraint (docs.retellai.com/build/dynamic-variables):
// every value under dynamic_variables MUST be a string — numbers/booleans
// are rejected. Cast everything explicitly.
return Response.json({
call_inbound: {
dynamic_variables: {
caller_name: caller?.name ?? "there",
caller_summary: memory?.summary ?? "",
caller_company: enrichment?.company ?? "",
is_returning_caller: String(Boolean(caller)),
},
},
});
}
// ── POST-CALL: Retell's call_analyzed webhook ────────────────────────────
// Fires call_started -> call_ended -> call_analyzed per call, for both
// inbound and outbound (payload shape is identical; only `direction`
// differs). Retell also documents an `x-retell-signature` header for
// authenticity — verify it in production (omitted here for brevity; use
// Retell's SDK verify() helper).
if (payload.event === "call_analyzed") {
const { call } = payload;
await mx.calls_end({
session_id: call.call_id,
phone_number: call.direction === "inbound" ? call.from_number : call.to_number,
// Verified shape (docs.retellai.com/api-references/get-call):
// transcript_object[].role is "agent" | "user" | "transfer_target",
// content is the turn text, words[] holds per-word {word, start, end}
// timestamps in seconds — Retell does not expose a single per-turn
// timestamp field, so we derive one from the turn's first word.
transcript: (call.transcript_object ?? []).map((turn: { role: string; content: string; words?: { start: number }[] }) => ({
role: turn.role === "user" ? "user" : "agent", // "transfer_target" folded into "agent"
text: turn.content,
ts_ms: turn.words?.[0]?.start ? Math.round(turn.words[0].start * 1000) : 0,
})),
duration_s: Math.round((call.duration_ms ?? 0) / 1000),
// Retell's call_analysis.call_successful is a boolean success signal,
// not a task outcome — Mnemix's outcome enum is task-shaped
// (appointment_booked / quote_given / callback_requested / no_answer),
// so map it into agent_metadata instead of forcing it into outcome.
outcome: "other",
agent_metadata: {
retell_agent_id: call.agent_id,
call_successful: call.call_analysis?.call_successful,
user_sentiment: call.call_analysis?.user_sentiment,
disconnection_reason: call.disconnection_reason,
},
});
return Response.json({ received: true });
}
return Response.json({ received: true });
}Works today — call the REST API directly:
# 1) Retell fires its Inbound Call Webhook BEFORE the call connects.
# (event: "call_inbound" — verified shape, docs.retellai.com/features/inbound-call-webhook)
curl -X POST https://your-app.com/webhooks/retell/inbound \
-H "Content-Type: application/json" \
-d '{
"event": "call_inbound",
"event_timestamp": 1780012672105,
"call_inbound": {
"agent_id": "agent_xxx",
"from_number": "+15551234567",
"to_number": "+15557654321"
}
}'
# Your handler calls Mnemix directly:
curl -X POST https://mcp.mnemix.ai/v1/recall_and_enrich \
-H "Authorization: Bearer $MNEMIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+15551234567", "trigger": "ringing"}'
# ...and responds to Retell (all dynamic_variables values MUST be strings):
# {
# "call_inbound": {
# "dynamic_variables": { "caller_name": "Jane Doe", "is_returning_caller": "true" }
# }
# }
# 2) Retell fires call_analyzed after the call ends (same shape for inbound
# and outbound; direction field differs). Your handler writes back:
curl -X POST https://mcp.mnemix.ai/v1/calls/end \
-H "Authorization: Bearer $MNEMIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "call_xxx",
"phone_number": "+15551234567",
"transcript": [
{"role": "agent", "text": "Hi, thanks for calling.", "ts_ms": 0},
{"role": "user", "text": "Hey, I called last week about my order.", "ts_ms": 3200}
],
"duration_s": 142,
"outcome": "other"
}'FAQ
- How long does the Retell integration take to wire up?
- About 15-20 minutes if you already have a deployed HTTP endpoint. It's two small handlers — no Custom LLM WebSocket takeover required — plus setting webhook_url in the Retell dashboard or agent config.
- Does this cover inbound and outbound calls?
- Yes. Retell's webhook payload shape is identical for both; only the direction field differs ("inbound" vs "outbound"). For outbound calls placed via Retell's Create Phone Call API, you can also call recall_and_enrich yourself before placing the call and pass the result straight into retell_llm_dynamic_variables on that API call, skipping the inbound webhook path entirely.
- What happens if enrichment misses — no record for this caller?
- recall_and_enrich returns an empty caller/memory gracefully rather than erroring. Your handler should fall back to safe default strings (e.g. caller_name: "there") so the call_inbound response is always valid and the agent proceeds normally.
- Can I bring my own enrichment vendor instead of Mnemix's bundled ones?
- Trestle and Twilio Lookup are the bundled providers in Wave 1. BYO vendors are supported on paid plans — contact hello@mnemix.ai to configure a custom enrichment source.
- What happens if Mnemix is down when Retell's call_inbound webhook fires?
- Non-blocking by design: if recall_and_enrich times out, return call_inbound with no dynamic_variables (or a cached fallback) so the call still connects on schedule. Failed call_analyzed write-backs retry on a queue rather than blocking the webhook response.
Last updated: .