API reference
The full public v1 surface is three endpoints. Base URL for all of them: https://mcp.mnemix.ai. Every request needs a bearer token:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx
Host migration: the canonical platform host is moving to
mcp.northsun.ai. During the transitionmcp.mnemix.aicontinues to serve the full surface unchanged — existing integrations keep working, no action required. When the successor host is generally available, responses from this host will carry aDeprecationheader (RFC 9745) and aLink rel="successor-version"header pointing at the correspondingmcp.northsun.aiURL; a retirement date, if one is ever set, will be announced in advance with aSunsetheader (RFC 8594). No cutoff is scheduled.
POST /v1/recall_and_enrich
The voice hot path. Call it once before the agent's first turn. It creates the caller record on a miss, recalls same-number memory, and returns cached or deadline-bounded enrichment when available.
Request body:
| Field | Type | Required | Notes |
|---|---|---|---|
phone_number | string | yes | Any format; normalized to E.164 server-side. |
trigger | "ringing" | "answered" | no | When in the call lifecycle this fired. |
session_id | string | no | Accepted correlation value; reuse it in calls/end when supplied. |
agent_id | string | no | Accepted agent identifier. Do not rely on persistence unless your tenant contract names it. |
metadata | object | no | Accepted context object. The frozen route does not guarantee that it is persisted. |
curl -sS -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": "answered",
"session_id": "call_abc123"
}'
Response — 200 OK:
{
"trace_id": "trace_...",
"known": true,
"caller": {
"name": "Mike Reynolds",
"first_seen_ms": 1750000000000,
"last_seen_ms": 1751000000000,
"total_calls": 3,
"tags": ["fleet", "commercial"],
"custom_attributes": {}
},
"memory": {
"summary": "Repeat customer, 3 calls over 2 months. Fleet account.",
"recent_calls": [
{
"call_id": "int_...",
"started_at_ms": 1750900000000,
"duration_s": 92,
"summary": "Confirmed brake inspection for Thursday",
"outcome": "appointment_booked"
}
]
},
"enrichment": {
"person": { "name": "Mike Reynolds", "title": null, "company": null },
"company": null,
"phone": { "carrier": "Verizon", "line_type": "mobile" }
},
"timing_ms": { "total": "<measured>", "memory_ms": "<measured>", "enrichment_ms": "<measured>", "cache_hit": true },
"freshness": { "memory_age_ms": null, "enrichment_age_ms": null }
}
timing_ms and freshness describe this request; they are not published performance benchmarks. A first-time caller returns known: false, an empty memory summary, and whatever enrichment completed within the request deadline. Individual enrichment blocks may be null.
If the caller-memory database read degrades after authentication, the endpoint preserves the 200 response shape with empty defaults and degraded: true. Authentication, validation, and rate-limit failures still return non-2xx errors and must be handled normally.
caller.total_calls can be null. null means the count was unavailable for that request — we could not read it and are telling you so, rather than guessing. It does not mean zero. Branch on it rather than coalescing it:
// Correct: "unknown" stays unknown.
const seenBefore = caller.total_calls === null ? null : caller.total_calls > 0;
// Wrong: turns "we don't know" into "this person has never called".
const seenBefore = (caller.total_calls ?? 0) > 0;
name, first_seen_ms and last_seen_ms use null the same way. The count is re-read on your next request — an unavailable count is never cached, so recovery does not wait on a TTL.
POST /v1/calls/end
The write-back. The durable write and the cache write-through the next recall reads from both complete before this call responds 204 — the next recall_and_enrich for that caller sees it immediately. A durable queue also receives the event as a retry net for reprocessing; that queue's own processing is asynchronous, but it is not what makes the call durable. See Hangup, and what happens next for the full guarantee.
Request body:
| Field | Type | Required | Notes |
|---|---|---|---|
session_id | string | yes | Reuse the paired recall session ID when one was supplied. |
phone_number | string | yes | Same caller. |
transcript | array of {role, text, ts_ms} | yes | role is "user" or "agent". Not a raw string. |
duration_s | number | yes | Call length in seconds, ≥ 0. |
outcome | string | yes | One of appointment_booked, quote_given, callback_requested, no_answer, other. |
agent_metadata | object | no | Arbitrary key-value passthrough. |
curl -sS -X POST https://mcp.mnemix.ai/v1/calls/end \
-H "Authorization: Bearer $MNEMIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "call_abc123",
"phone_number": "+15551234567",
"transcript": [
{ "role": "agent", "text": "Thanks for calling, how can I help?", "ts_ms": 0 },
{ "role": "user", "text": "I need to confirm Thursday.", "ts_ms": 3200 }
],
"duration_s": 92,
"outcome": "appointment_booked"
}'
Response — 204 No Content. No body. This confirms the interaction is already committed durably — see Hangup, and what happens next for the full guarantee. Retain the session trace for troubleshooting.
GET /v1/caller/
Read-only profile fetch. No side effects — doesn't trigger enrichment, doesn't create a contact on a miss.
curl -sS https://mcp.mnemix.ai/v1/caller/%2B15551234567 \
-H "Authorization: Bearer $MNEMIX_API_KEY"
Response — 200 OK:
{
"name": "Mike Reynolds",
"first_seen_ms": 1750000000000,
"last_seen_ms": 1751000000000,
"total_calls": 3,
"tags": ["fleet", "commercial"],
"custom_attributes": {}
}
404 if the number has never called — this endpoint never auto-creates. Use recall_and_enrich for that.
total_calls follows the same rule as on recall_and_enrich: null means the count was unavailable, never zero.
Errors
Every non-2xx response carries a machine-readable code:
{ "error": { "code": "validation_error", "message": "..." } }
| Status | Code | Meaning |
|---|---|---|
400 | bad_request | Malformed input — e.g. an unparseable phone number. |
401 | unauthorized | Missing or invalid bearer token. |
404 | not_found | GET /v1/caller/{phone_number} on an unknown caller. |
422 | validation_error | Request body fails schema validation. |
429 | rate_limited | Token bucket exhausted; back off and retry. |
500 | internal_error | An unexpected server error occurred; retain the request ID. |
503 | service_unavailable | A required dependency was unavailable; retry with backoff. |
Machine-readable spec
The public site exposes an OpenAPI document at /.well-known/openapi.json for the frozen paths and request bodies. Pin the document you inspect. Current response schemas are intentionally permissive, so verify generated response types against the examples and live contract before relying on them.
Next steps
- What recall gives you back — the full response shape annotated field-by-field, including the additive
factsfield (conditional; absent from production today). - Hangup, and what happens next — the read-after-write guarantee behind
calls/end's204. - MCP server — connect through hosted MCP and distinguish stable caller tools from evolving context tools.
- What is caller memory? — the concept behind
recall_and_enrich.