This walkthrough wires Mnemix into a Vapi assistant so every inbound call starts with caller context and every completed call makes the next one smarter. It works with any Vapi setup that can hit a webhook or call a tool.
What you need
- A Vapi assistant taking inbound calls
- A Mnemix API key (Hobby $0)
- Somewhere to run ~20 lines of glue (your existing server, or a serverless function)
1. Recall on ring
In your server-url handler, when Vapi signals an inbound call, recall before the assistant speaks:
const recall = await fetch("https://mcp.mnemix.ai/v1/recall_and_enrich", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MNEMIX_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
phone_number: call.customer.number,
trigger: "answered",
session_id: call.id,
}),
}).then((r) => r.json());
2. Hand the context to the assistant
Inject what came back into the assistant's system context:
const context = recall.known
? `Caller: ${recall.caller.name ?? "name unknown"}. ` +
`History: ${recall.memory.summary || "no prior calls"}.`
: "First-time caller — no history.";
Now "Hi, thanks for calling" becomes "Hi Mike — calling about Thursday's inspection?"
3. Write back on end-of-call
When Vapi sends the end-of-call report, persist it:
// Vapi's endedReason tells you *how* the call ended, not what it achieved,
// so business outcomes (appointment_booked, quote_given, callback_requested)
// come from Vapi's analysis.structuredData — configure your assistant's
// structuredDataPlan to extract an "outcome" field. endedReason still covers
// the no-answer cases. The @mnemix-ai/vapi-kit package does this mapping
// (plus fuller transcript normalization) for you if you'd rather not
// hand-roll it.
function toMnemixOutcome(report) {
const extracted = report.analysis?.structuredData?.outcome;
if (["appointment_booked", "quote_given", "callback_requested"].includes(extracted)) {
return extracted;
}
const reason = (report.endedReason ?? "").toLowerCase();
if (reason.includes("no-answer") || reason.includes("voicemail")) return "no_answer";
return "other";
}
await fetch("https://mcp.mnemix.ai/v1/calls/end", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MNEMIX_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
phone_number: call.customer.number,
session_id: call.id,
duration_s: Math.round(report.durationSeconds ?? 0),
outcome: toMnemixOutcome(report),
// report.messages is Vapi's structured turn list (artifact.messages);
// Mnemix wants { role: "user" | "agent", text, ts_ms }[], not a raw string.
transcript: (report.messages ?? [])
.filter((m) => m.role === "user" || m.role === "bot" || m.role === "assistant")
.map((m, i) => ({
role: m.role === "user" ? "user" : "agent",
text: m.message,
ts_ms: i * 1000,
})),
}),
});
Mnemix stores the call as a durable interaction keyed to the caller — the next recall_and_enrich for this number returns it as history.
That's the whole integration
One call at ring, one at hang-up. The second time a customer calls, your assistant remembers them — and the loop compounds from there.