Predictive engine
Customers ring first. Agents join only on answer.
Same dialer_master_data and dialer_priority pool as Progressive. Originate is AMI, not call.php. Lead ingest lives in the API reference.
End-to-end flow
A 1 second pacer counts idle skilled agents on this module’s dialer_employee roster, claims unassigned leads from the master table, and originates the customer on a Local channel. Skills come from dialer_employee.skills (JSON list matched to lead form_type). Pinned leads (agent_id = dialer_employee.id) are claimed only for that agent, not from the overdial pool. AMI ignores non-roster extensions. The agent panel stays on waiting until a customer answers and is bridged.
Pacing
target = min(max(idle, floor(idle × predictive_ratio)), max_lines). Extra legs are target − (ringing + on-hold). Ratio is at least 1. There is no abandon-rate brake. After a connected call the agent is not idle until buffer_time wrap finishes. Dashboard On hold is the count of answered customers still on MusicOnHold.
Claim order
Same SQL as Progressive: FOR UPDATE SKIP LOCKED, then Calling and reserved. Pool claims keep agent_id at 0 until a human is actually bridged. Any master row with agent_id is claimed only when that agent is idle and is bridged only to them — it never enters the skill pool.
Lowest
priority_no first.Pending, then Callback, then Abandoned, then Not Connected.
Never attempted first, then oldest attempt, then oldest push, then lowest id.
Received, Rejected, Calling, and reserved rows are skipped. Abandoned and Not Connected wait on next_retry_at (2-2-1 + working hours). Claim refuses originate outside the work window except for a due callback. priority_id 1, 2, or 3 wait on callback_scheduled_at. Pinned leads (agent_id set) are documented in the Callback Request guide.
AMI originate
Customer originate is Local/{0number}@from-internal/n with Application: MusicOnHold and OLI_ID. AMI_OUTBOUND_CHANNEL (for example PJSIP/9001) identifies the trunk in AMI events; it is not the Originate channel. 10-digit numbers get a leading 0.
When a customer answers, the engine originates PJSIP/{extension} with Application: Bridge. agent_id is written only after that bridge succeeds.
Hold until agent
If nobody matching the skill is free, or the agent is already on another predictive call, the extra customer stays on MusicOnHold. Only one bridge originate is sent to an agent at a time. If that agent declines (SIP deny), the customer is hung up and that extension is not re-offered for the same hold. Waiting legs are drained when another eligible agent becomes idle. Hold is capped by hold_timeout_seconds on fic_dialer_config (default 30). Only then is the customer hung up and the lead settled Abandoned.
Hangup results
| Hangup | Stored status |
|---|---|
| Answered and talked | Received (keeps agent_id) |
| Answered, hold timeout / customer left before an agent | Abandoned |
| Cancel by customer, invalid destination | Rejected |
| No answer, busy, congestion, chanunavail | Not Connected |
Abandoned and Not Connected use the same 2-2-1 scheduler as Progressive: retry_pattern (default [2,2,1]), same-day retry_seconds, snap to next work_start after work_end. Final Disposal only when call_count reaches max_tries. Received, Rejected, and Disposed are not claimed again. Pinned callbacks keep agent_id and use the same retry policy.
Engine config
| Setting | Where | Meaning |
|---|---|---|
dialer_type | fic_dialer_config | Set to Predictive |
buffer_time | fic_dialer_config | Wrap after a connected call |
predictive_ratio | fic_dialer_config | Customer legs per idle agent (default 1.20) |
hold_timeout_seconds | fic_dialer_config | Max customer wait on hold (default 30) |
max_lines | fic_dialer_config | Cap on ringing + hold customer legs |
max_tries | fic_dialer_config | Hard cap on originates (keep in sync with sum of retry_pattern) |
retry_seconds | fic_dialer_config | Same-day gap after Not Connected or Abandoned |
retry_pattern | fic_dialer_config | Per working-day caps, default [2,2,1] |
work_start / work_end | fic_dialer_config | Originate window, default 09:30–18:30 IST |
work_days | fic_dialer_config | ISO weekdays, default Mon–Sat [1,2,3,4,5,6] |
AMI_OUTBOUND_CHANNEL | .env | Trunk identity, e.g. PJSIP/9001 |
AMI_AGENT_CHANNEL | .env | Default PJSIP/{extension} |
PREDICTIVE_CONNECT_TIMEOUT_MS | .env | Agent ring / answer window (default 30s) |
CALL_API_URL | .env | Not used in this mode |