Progressive engine
One free agent. One next lead. No overdial.
This guide is only the calling brain. Lead ingest lives in the API reference. Overdial and customer-first originate are in the Predictive guide.
End-to-end flow
The engine does not poll on a global timer. Every AMI patch, panel join/leave, or ready/pause updates that extension and may schedule a claim.
When an agent dials
| Flag | Source | Must be |
|---|---|---|
panel | Agent page connected | active |
presence | Ready / pause on the panel (dialer_employee.panel_status) | ready |
ami | Asterisk device state | idle |
Also required: no call already in flight, employee loaded from this module’s dialer_employee roster, and at least one skill in dialer_employee.skills (JSON list matched to lead form_type). Ready/pause writes dialer_employee.panel_status. AMI and the dashboard only track roster extensions. After a call, the engine waits buffer_time seconds, then claims again. If no lead matches, it retries every 10 seconds.
Claim order
Two agents cannot take the same row. Claim uses FOR UPDATE SKIP LOCKED, then sets Calling, reserves the lead, increments call_count, and sets first_attempt_at on the first originate.
If
agent_id is set, only that employee claims and originates it. Skill match is skipped.Lowest
priority_no first (Callback Request is 1).Pending, then Callback, then Abandoned, then Not Connected.
Never attempted first, then oldest attempt, then oldest push, then lowest id.
Unassigned leads (agent_id null or 0) must match form_type to the agent’s skills. Any master row with agent_id is originated only on that agent’s extension. Received, Rejected, Calling, and reserved rows are skipped. Claim also waits on next_retry_at and refuses originate outside work_start–work_end except for a due callback.
Callback Request
priority_id 1, 2, or 3 honor callback_scheduled_at. A callback time requires agent_id. Full behaviour (time gate, pin, retry) is in the Callback Request guide. How you send the fields is in the API reference.
Future
callback_scheduled_at → skip. Other priorities keep dialing.Only that
agent_id claims it. It is first in their queue.Priority 1–3 without a datetime is Pending. If
agent_id is set, only that agent dials it; otherwise any matching skill.Hangup results
| Hangup | Stored status |
|---|---|
ANSWER | Received (keeps agent_id) |
| Cancel by customer, invalid destination | Rejected |
| No answer, busy, congestion, chanunavail | Not Connected |
Received, Rejected, and Disposed are not claimed again. Not Connected and Abandoned use a 2-2-1 scheduler: retry_pattern (default [2,2,1]) caps tries per working day from first_attempt_at; retry_seconds is the same-day gap; a slot after work_end (default 18:30 IST) snaps to the next working day’s work_start. Final Disposal only when call_count reaches max_tries. Received keeps agent_id. Pinned callbacks keep agent_id on every outcome and use the same retry policy.
Engine config
| Setting | Where | Meaning |
|---|---|---|
dialer_type | fic_dialer_config | Progressive or Predictive |
buffer_time | fic_dialer_config | Seconds between calls per agent |
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] |
disposed_priority_id | fic_dialer_config | Final Disposal — never claimed |
CALL_API_URL | .env | Originate URL (call.php) |
DIAL_ORIGINATE_TIMEOUT_MS | .env | Wait for AMI on_call (default 60s) |