receive_call_results(); storage logic lives in the call service.
The endpoint requires
X-CXBCore-Secret (403 on mismatch). The body is validated against the CallResultsPayload model and dumped with exclude_none=True before storage, so absent fields never overwrite the call skeleton with nulls.Payload
CallResultsPayload carries the final call state. Key fields:
| Field | Notes |
|---|---|
session_id, stream_id, caller_id, from_number | Identity / linkage (matched on session_id). |
call_duration_seconds, disconnected_by, call_direction | Outcome basics. |
transcript, events, intents | Conversation timeline. |
recording_url, recording_key, recording_access_token | Recording references. |
post_call_analysis, qc_analysis | Raw LLM dicts (schema defined by the bot prompt). |
latency, latency_samples, usage_metrics, llm_cache_summary | Telemetry / cost. |
pipeline_config, connected_event | Diagnostic snapshots. |
was_transferred, transfer_* | Native transfer state derived by CXB Core. |
Storage
store_call_results() updates db.calls by session_id (upsert) and:
- Recording token: if a recording is present but no
recording_access_token, it reuses the existing call’s token or mints a newsecrets.token_urlsafe(24). This token backs the stable CRM-facing recording URL. - Status derivation from
disconnected_by:
disconnected_by | Stored status |
|---|---|
error, no_answer, rejected | failed |
voicemail | voicemail |
| (anything else) | completed |
- Cache summary:
build_llm_cache_summary()aggregatespost_call/qcusage entries into token-only facts (cache_read_input_tokens,cache_creation_input_tokens,fresh_prompt_tokens,completion_tokens, plusenabled/worked/statuses). It never reports money. - Sets
completed_at.
A
connected_event of None is popped before storage so it never clobbers the connected event captured on the call skeleton at config time.Fan-out
After storage, the call doc is reloaded bysession_id and two background tasks may be scheduled:
- Campaign reconciliation —
process_campaign_call_resultruns for every call (it no-ops when the call is not campaign-linked). See Campaign API and Callbacks. - CRM post-call push — runs only when the bot has a
post_call_pushconfig and the call is not flaggedskip_post_push. It passes the bot timezone, systemminioconfig, and the request’s public base URL. See CRM push.
{ "status": "ok", "session_id": ... } immediately; the fan-out runs in FastAPI background tasks.
Operational checks
| Symptom | First place to check |
|---|---|
| Result not stored | X-CXBCore-Secret, payload validation errors, CXB Core result outbox. |
Call stuck active | Result webhook never delivered (CXB Core outbox), or wrong webhook_url. |
| Wrong status | disconnected_by value mapping above. |
| No CRM push | Bot post_call_push config, skip_post_push flag, _skip_post_push control flag at config time. |
| Recording link missing on push | recording_access_token / recording_key on the call doc. |
Related docs
CXB Core post-call
What CXB Core packages before posting results.
CRM push
Mapping results into the external CRM payload.
CDR ingestion
Merging WebSocket-transport CDRs into call records.
Recordings proxy
Token-based stable recording URLs.