whatsapp_voice — escalate to a phone call when the conversation calls for it. This guide covers the message flow, the two timers that govern sends, templates, and inbound handling.
The whatsapp_voice flow
Each recipient runs through send → wait → decide → act:
1
Send template
The campaign re-checks opt-out status and required template variables, then sends the approved template. The provider receives only the template variables; Lehar renders the approved body into the stored transcript for the dashboard and the decision model.
2
Wait
Waits for a reply, a terminal delivery failure, or the response timeout — sending up to
reminder_count template reminders while it waits.3
Decide
A deterministic opt-out keyword check runs first (no model). Then an optional knowledge base lookup, then a structured LLM decision →
call | reply | opted_out | no_call_required. Only this campaign’s messages feed the decision context, so past campaigns don’t leak in.4
Act
call hands off to the outbound voice loop; reply sends an in-window free-form answer; opted_out / no_call_required / timeout finalize the recipient.Two independent timers
- 24-hour service window — whether a free-form message may be sent at all. It’s opened by the recipient’s last inbound message and is re-validated against the clock at send time. Outside the window, only approved templates may be sent.
response_timeout_seconds— the orchestration timer for how long to wait before a reminder or giving up. Independent of the window.
reply is free-form (the window is open because the recipient just replied).
Templates
WhatsApp sends use pre-approved templates from a customer-scoped registry. Administrators add approved templates per workspace; customers get a read-only view that feeds the dashboard dropdown and CSV columns.
A template carries
template_id, optional name, language, category, variables[], and an optional body_template (the approved body, rendered into internal transcripts). The campaign’s workflow.whatsapp config maps each template placeholder to a recipient variable or a literal fallback.
Configuration shape
whatsapp_voice config is stored on the campaign under workflow.whatsapp:
Inbound messages
Replies and delivery receipts arrive atPOST /webhooks/whatsapp/inbound (provider-authenticated). Handling is idempotent on the provider’s message id and applies delivery statuses monotonically (a read is never regressed to delivered). An inbound reply opens the service window and wakes the waiting recipient. See Webhooks & Events.
Finalization states
A recipient finalizes as one of:completed, no_response, opted_out, no_call_required, or failed. An opted_out result also writes a workspace-level suppression so future campaigns honor it. The call branch finalizes through the reused voice path.
Reading conversations
GET /campaigns/{id}/events includes the structured campaign.whatsapp_decision entries (decision, reason, and reply text), which is the clearest way to audit why a recipient was called, answered, or suppressed.
