Get started · 9 / 42

What recall gives you back

recall_and_enrich returns trace_id, known, caller, memory (summary + recent_calls, newest call first, up to five), enrichment, timing_ms and freshness. context_audit_id, memory.facts and memory.fact_ids are optional additive fields: present only when the matching feature is on for your deployment, absent otherwise.

This is the exact response shape POST /v1/recall_and_enrich returns, read from the route handler rather than an older document. Every field below is either always present or explicitly marked optional.

The shape

The caller, memory, and enrichment values below are an illustrative example, not a real caller — the same convention used on API reference.

{
  "trace_id": "b6b6c6d2-4f1e-4a57-9d3b-0c9a1f6e2a10",
  "context_audit_id": "b6b6c6d2-4f1e-4a57-9d3b-0c9a1f6e2a10",
  "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": "7f0c2a64-3b1d-4c8e-8a5f-2d9e1b6c4a37",
        "started_at_ms": 1750900000000,
        "duration_s": 92,
        "summary": "Please confirm my 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": "<measured>", "enrichment_age_ms": null }
}
BlockWhat it is
trace_idCorrelation ID for this request. Log it; it's what support needs to find the request.
context_audit_idOptional and additive: present only when the voice feedback loop is enabled for your deployment, absent otherwise. When present it equals trace_id. Do not require it.
knownWhether a caller record existed before this request — not whether enrichment succeeded.
callerStored profile: name, first_seen_ms, last_seen_ms, total_calls, tags, custom_attributes. total_calls (and the other fields) can be null — that means "unavailable for this request," never "zero" or "none." See API reference for the branching rule.
memory.summaryA short rolling summary. Empty string for a brand-new caller.
memory.recent_callsCall history, newest call first (by when it was recorded), up to five, not ranked by relevance to the current call. Each entry's summary is the text recorded for that call. Your application decides what's worth surfacing.
enrichmentperson, company, phone — each independently nullable. A null block means that source didn't complete inside the request deadline, not that the caller has no such data.
timing_mstotal, memory_ms, enrichment_ms, cache_hit for this request. A measurement, not a published benchmark — public latency language stays designed for sub-300ms voice recall.
freshnessmemory_age_ms, enrichment_age_ms — how old the served data was when it was served, or null when that isn't known.
degradedPresent and true only when an authenticated caller-memory read failed and the route returned the safe empty-shape fallback instead of erroring. Absent on a normal response.

The optional facts field

The response can carry one more field: memory.facts, governed fact strings read from stored memories (the governed-fact substrate described in Locked facts is separate). It is an optional additive field: when the governed-facts read is not enabled for your deployment, the memory block has exactly the fields above, with no facts key at all, so existing integrations parse the same response shape either way. When the field is present its value can be an array, null, or "denied". Alongside it, memory.fact_ids is likewise optional and additive: when present, fact_ids[i] is the memory id of facts[i], so a feedback call can name the memories it refers to.

When facts is present, its value has these meanings:

ValueMeaning
absentThe field is not served in this response. Do not read anything into it.
[]The read ran, and this caller has no governed facts. A measurement.
nullThe read did not complete — it exceeded its internal budget, or errored. Unavailable, not empty.
"denied"Governed facts were refused for this caller by an authority check, and no governed-facts read ran. Not empty and not a failed read: say nothing about what you do or don't remember.

null is deliberate, and it is the one to branch on. The governed read is bounded so that a slow read can never delay the voice response, which means it can be dropped while the rest of the packet is served normally under a 200. If a dropped read reported [], a caller whose history we hold would be indistinguishable from a caller we have never seen. So the packet says null instead. Do not coalesce it — facts ?? [] throws away exactly the distinction the field exists to carry.

Treat context_audit_id, facts and fact_ids as optional in your parser: build against the always-present fields, and use the optional ones only when they appear.

Put this in a prompt without wrecking the agent

  • Don't dump the JSON. Select the two or three fields that change what the agent should say — usually caller.name, memory.summary or the top of memory.recent_calls, and one enrichment field — and render them as short natural-language lines, the way the Vapi and Bland kits already do with prompt variables.
  • Never block the greeting on enrichment. enrichment fields are independently nullable by design; a cold caller with all three null is a normal response, not a failure. Give the agent a useful fallback line for that case.
  • Don't treat recency as relevance. recent_calls is ordered newest call first, not by what matters to today's call — filter or pick in your application before it reaches the prompt.
  • Branch null correctly. caller.total_calls: null means "unknown," not zero — a prompt that says "this is your first call" on a null count asserts something you don't know. memory.facts: null is the same rule: it means the governed read did not complete (and "denied" means it was refused), so say nothing about what you do or don't remember rather than implying you have no history.
  • Treat degraded: true as a signal to soften, not hide. The shape is still safe to render; just don't present a degraded read as complete history.

Next steps