After a call’s results are stored, CXB API can push a mapped payload to an external CRM. The mapping is fully config-driven via the bot’s post_call_push config and a key_mapper. Orchestration is run_post_call_push() in the call service; the HTTP send is push_post_call_data() in the CRM service.

Trigger

The push is scheduled from results ingestion only when the bot has a post_call_push config and the call is not flagged skip_post_push. It runs as a background task with the bot timezone, system minio config, and the request’s public base URL.

Recording URL substitution

Before building variables, run_post_call_push rewrites the recording reference:
  1. If a recording_key + recording_access_token + public base URL are all available → use the stable token URL (/api/v1/public/recordings/{token}).
  2. Otherwise, if a recording_url + minio config exist → generate a signed URL as a fallback (for older call docs without a token).
See Recordings proxy.

Variable assembly

build_push_variables(call_doc, payload, tz_name) builds the full {{namespace.field}} context the key mapper resolves against:
NamespaceSource
call.*Stored connected_event + filtered SIP-header variables. Also call.sip_headers_json (all SIP headers as one JSON object).
crm.*The pre-call CRM fetch (crm_data).
result.*System-generated: call_id, recording_url, call_duration_seconds, disconnected_by, disposition, start/end/answer timestamps (bot timezone), transcript (formatted JSON), and the full transfer_* set.
analysis.*LLM post-call analysis. All known keys are pre-seeded to "" so unresolved placeholders never leak as literals.
qc.*LLM QC analysis (empathy_score, compliance_score, overall_score, remarks), pre-seeded to "".
result.disposition is a coarse system label derived from disconnected_by (customer/bot/timeoutConnected, errorFailed, else Unknown) — distinct from the LLM analysis.DISPOSITION. result.call_answer_time equals the start time for the WebSocket transport, which does not report answer time separately.

Template resolution and drop_unresolved

push_post_call_data renders every key_mapper template with render_template(..., drop_unresolved=True):
  • Resolution tries the exact {{ns.field}} key, then a suffix fallback across system.crm.call. by priority. So {{CUSTOMERNAME}} can resolve from crm.CUSTOMERNAME, and {{crm.X}} can fall back to call.X.
  • With drop_unresolved=True, an unresolved {{...}} becomes "" instead of being preserved as a literal — so a missing variable sends an empty string rather than leaking the template to the CRM.
  • _coerce_json_like parses values that look like JSON ({...}/[...]) back into objects/arrays, so a mapped field like callingheaders can be sent as a real object when the CRM requires it.

Send and logging

Setting (in post_call_push)Use
urlDestination (no url → push skipped).
methodPOST (JSON body) or GET (query params). Default POST.
token / headersheaders if provided, else Authorization: {token}.
key_mapper{ external_key: "{{template}}" } map producing the payload.
The result log (status = ok/rejected/skipped/failed, http_status, truncated response_body) is stored on the call doc as post_call_push_result, appended to integration_log, and a post_call_push event is pushed onto events with a relative timestamp. Request timeout is 10s.
The pre-call CRM fetch (fetch_crm_data in the CRM service) is a separate path with a 2s timeout, used at config time to populate crm.*. This page covers the post-call push direction.

Operational checks

SymptomFirst place to check
Push not sentBot post_call_push.url, skip_post_push flag.
CRM rejects payloadkey_mapper keys vs CRM schema, post_call_push_result.response_body.
Literal {{...}} reached CRMShould not happen under drop_unresolved; check the mapper key prefix.
Field sent as string instead of object_coerce_json_like only parses values starting with {/[.
Recording link broken at CRMToken URL vs signed URL fallback; see Recordings proxy.

Post-call variables

Full key_mapper variable catalog.

Results ingestion

What triggers the push.

Recordings proxy

Stable recording URL substitution.

Post-call webhook

Operator-facing webhook configuration.