When a customer asks to be called back, CXB API extracts that intent from the post-call analysis, resolves a concrete callback time, and reschedules the contact. This is the CXB API half of the feature; the operator-facing view is in Operations → Callbacks. Logic lives in the campaign-callback service and the campaign-result service; the live-prompt gate is in the config service.

The double gate

Callbacks only schedule when both gates are satisfied:
GateFieldOwner
Bot togglebot.callback_prompt_injectionBot config
Campaign flagcampaign.config.callback_detection.enabledCampaign config
In the config service, prompt injection runs only when both are true (_callback_detection_enabled + callback_prompt_injection). The runtime config exposes callback_detection_enabled so CXB Core can run dedicated callback extraction. See Conversation policy → callback prompt injection.

Extraction

extract_callback_request(analysis) reads only the standard analysis keys (no keyword scraping):
OutputFrom analysis key
requested (bool)callback_requested
preferred_time_textcallback_preferred_time_text
reasoncallback_reason
confidence (float)callback_confidence
should_schedule_callback(campaign, analysis) = campaign gate AND requested.

Time resolution

resolve_callback_time(preferred_time_text, requested_at, campaign) converts the customer’s spoken timing into a concrete UTC datetime, in the campaign timezone (config.time_window.timezone, default Asia/Kolkata). Resolution order:
  1. Relative offset_relative_delta parses “5 min”, “2 hours”, “aadhe ghante”/“half an hour” → requested_at + delta.
  2. Daypartsubah/morning → 10:00, shaam/evening → 18:00, with day offset markers kal/tomorrow (+1) and parso/day after (+2).
  3. Day offset only → next day(s) at 10:00.
  4. Fallback — no clear time → requested_at + retry_delay_minutes, with reason NO_CLEAR_TIME_REASON.
Every resolved time is clipped to the campaign window (_clip_to_window); a time outside hours is pushed to the next window start with OUTSIDE_WINDOW_REASON.

Persistence

In process_campaign_call_result, when scheduling wins (and the campaign was not manually stopped, and reschedule count < MAX_CALLBACK_RESCHEDULES = 5), the contact is updated atomically (matched on the _processing lock):
  • Status → callback_scheduled, next_retry_at = resolved time.
  • A callback sub-doc: active, requested, status: scheduled, sequence, requested_at, scheduled_at, preferred_time_text, reason, confidence, source_session_id, source_attempt, exceeds_max_attempts, fallback_reason.
  • A new entry is pushed onto callback_history (with callback_attempt = current_attempt + 1).
  • When rescheduling from a prior callback, the previous callback_history entry is closed (completed/exhausted) via an array_filters update.
Stats increment callback_requested + callback_scheduled; rescheduling from an existing callback also nets callback_completed/callback_connected so pending counts stay balanced.
Callback scheduling takes priority over normal retry logic — a requested callback supersedes the standard redial_rules retry decision for that contact.

Stopping callbacks

stop_campaign cancels scheduled callbacks: contacts in callback_scheduled go to manual_stopped with callback.status: cancelled (and open callback_history entries closed). Lease priority in CXB Dialler dials callbacks first. The GET /{id}/calls?callback= filter supports requested / pending / completed / cancelled views.

Operational checks

SymptomFirst place to check
Callbacks never scheduledBoth gates (callback_prompt_injection + campaign callback_detection.enabled).
LLM didn’t emit callback keysPrompt injection — the analysis prompt must already lack callback_requested/callback_preferred_time_text for injection to apply.
Wrong callback timepreferred_time_text parsing + campaign timezone + window clipping.
Callback loopsMAX_CALLBACK_RESCHEDULES = 5 cap.
Callbacks dialled out of hours_clip_to_window and the campaign time_window.

Operations: Callbacks

Operator-facing callback behaviour and tracking.

Campaign API

Campaign lifecycle, stats, and result reconciliation.

Conversation policy

Callback prompt injection into the analysis prompt.

CXB Core post-call

The dedicated callback extraction LLM call.