Retell AI + ContextDB
Retell handles the call. ContextDB remembers the caller.
Load caller history for a Retell call, evaluate memory policy inside a custom function, enforce host identity and current state, and save sourced facts from the post-call webhook.
evaluate_action before any booking or account change, then
call report_execution after each outcome.
recall_for_action is a compatibility retrieval helper, not
host authorization. Examples use the public Cloud client against
ContextDB Cloud. The local engine
uses pycontextdb. This independent guide has no vendor
partnership or endorsement.
Backend setup
Install the Cloud client first.
The handlers below are adapter patterns, not a standalone Retell app. Connect the named authentication, identity, and calendar functions to your backend before running them.
python -m venv .venv source .venv/bin/activate pip install contextdb-cloud-client==0.2.0a2 fastapi uvicorn export CONTEXTDB_API_KEY="your-project-key" export CONTEXTDB_BASE_URL="https://api.contextdb.ai"
Run the ContextDB Cloud quickstart before adding the Retell webhook → Try a memory scenario in Playground →
During the call
Check the customer's confirmed details before booking.
Retell custom functions call your HTTPS endpoint mid-conversation and speak the result. Your endpoint authenticates the function request, derives the caller from the verified call, checks host permissions and the current slot, then branches on the durable ContextDB decision.
# FastAPI endpoint behind a Retell custom function # name: check_before_booking from contextdb_cloud_client import CloudClient from fastapi import HTTPException, Request @app.post("/retell/check-before-booking") async def check_before_booking(req: Request): function_call = await authenticate_retell_function(req) args = function_call.args caller = await identities.customer_for_call(function_call.call_id) slot = await calendar.get_slot(args["service"], args["day"]) authorized = await calendar.can_book( actor_id=function_call.actor_id, customer_id=caller, slot=slot, ) if not authorized: raise HTTPException(status_code=403, detail="not authorized") async with CloudClient(BASE_URL, api_key=KEY) as cdb: decision = await cdb.evaluate_action( caller, f"book {args['service']} on {args['day']}" ) if decision.outcome == "act": try: booking = await calendar.book( caller, args["service"], args["day"], expected_version=slot.version, ) except SlotChanged: await cdb.report_execution( caller, decision.decision_id, "appointment.book", "failed", idempotency_key=f"retell-receipt-{decision.decision_id}", error_code="current_state_changed", ) return {"result": "the slot changed, so nothing was booked"} await cdb.report_execution( caller, decision.decision_id, "appointment.book", "succeeded", idempotency_key=f"retell-receipt-{decision.decision_id}", external_ref=booking.ref, ) return {"result": f"booked; reference {booking.ref}"} if decision.outcome == "ask": await cdb.report_execution( caller, decision.decision_id, "appointment.book", "skipped", idempotency_key=f"retell-receipt-{decision.decision_id}", ) return {"result": "ask the caller to confirm the day"} if decision.outcome == "abstain": await cdb.report_execution( caller, decision.decision_id, "appointment.book", "skipped", idempotency_key=f"retell-receipt-{decision.decision_id}", ) return {"result": "do not book; offer a human handoff"} raise RuntimeError("unknown ContextDB action outcome")
After the call
Save the important customer details after the call.
Retell posts call events to your webhook when the call ends. Store the
durable fact with its source and keep the returned memory ID. The
user_stated source records who said it. It does not set
confirmed. Call confirm against that exact
ID only after an authenticated actor explicitly attests.
@app.post("/retell/webhook")
async def on_call_ended(req: Request):
event = await authenticate_retell_webhook(req)
if event.event != "call_analyzed":
return {"ok": True}
caller = await identities.customer_for_call(event.call_id)
instruction = require_user_quoted_instruction(event)
async with CloudClient(BASE_URL, api_key=KEY) as cdb:
# remembering the statement does not confirm it
saved = await cdb.remember(
caller,
instruction.content,
source="user_stated",
confidence=0.6,
action_relevant=True,
idempotency_key=f"retell-{event.call_id}-instruction",
)
await confirmation_store.bind(event.call_id, caller, saved.id)
return {"ok": True, "pending_memory_id": saved.id}
@app.post("/retell/confirm-memory")
async def confirm_memory(req: Request):
attestation = await authenticate_retell_attestation(req)
if not attestation.actor_said_yes:
raise HTTPException(status_code=400, detail="explicit yes required")
caller = await identities.customer_for_call(attestation.call_id)
pending = await confirmation_store.get(attestation.call_id)
if pending.customer_id != caller:
raise HTTPException(status_code=403, detail="identity mismatch")
async with CloudClient(BASE_URL, api_key=KEY) as cdb:
await cdb.confirm(
caller,
pending.memory_id,
idempotency_key=f"retell-confirm-{pending.memory_id}",
)
return {"ok": True, "confirmed_memory_id": pending.memory_id}
Retell call order
Add memory at four points in the Retell call.
| Moment | Retell surface | ContextDB call |
|---|---|---|
| Call connects | Inbound webhook / dynamic variables | recall → caller snapshot into the prompt |
| Agent wants to book, refund, change | Custom function | evaluate_action → branch, then report_execution |
| Caller says an explicit yes | Custom function | confirm → record the authenticated attestation |
| Call ends | Post-call webhook | remember with source and confidence |
Retell implementation
Where caller memory fits in a Retell call.
Does this add latency to the conversation?
Ordinary turns are untouched. The model talks as fast as Retell lets it. The gate runs only inside custom functions guarding consequential actions, where a beat of "let me check that for you" is natural and the alternative is booking the wrong day.
Should I store the Retell transcript in ContextDB?
No. Keep transcripts and recordings in Retell or your archive. Store the extracted, action-relevant facts with provenance, and keep the call ID in your own systems as the pointer back to the transcript.
How do I identify the caller across calls?
Use a stable identifier you control, such as your customer ID resolved
from a verified Retell call and your own identity mapping, as the
ContextDB user_id. Do not accept the partition from
custom-function model arguments.
Build the Retell memory flow with the SDK.
Start with recall, then gate one custom function before it books.