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. Both engines originate through AMI.
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. Inbound on that desk blocks outbound. So does an overdue missed activity (outbound_blocked); the only outbound allowed then is a redial of the agent's own missed OLI.
- Agent socket + AMI state (panel active, presence ready, SIP idle)
- Wait buffer_time after the last connected call
- Claim next skill-matched lead (Callback → Pending → Abandoned → Not Connected; NC uses Fresh Assign + call_count)
- AMI Originate to PJSIP/{extension} into from-internal
- Talk → Received. Miss → Not Connected. Then wrap and claim again
When an agent dials
| Flag | Source | Must be |
|---|---|---|
panel | Agent page connected | active |
presence | Ready / pause on the panel (sales_dialer_employee.panel_status) | ready |
ami | Asterisk device state | idle |
Also required: no call already in flight, employee loaded from this module’s sales_dialer_employee roster, and at least one skill in sales_dialer_employee.skills (JSON list matched to lead form_type). Ready/pause writes sales_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 and reserves the lead (isReserved = 1, reserved_at). first_attempt_at (first dial only) and last_attempt_at are stamped right before the real originate, so a claim released before dialing leaves no attempt history. call_count does not increment on claim.
Skill:
form_type ∈ agent skills. Priority: priority_id ∈ agent priority_ids (empty list = all priorities).Callback (due), then Pending, then Abandoned, then Not Connected last.
Callback / Pending / Abandoned: lowest
priority_no. Not Connected: sooner next_retry_at, then Fresh Assign, then lowest call_count.Never attempted first, then oldest attempt, then oldest push, then lowest id.
Unassigned leads must match form_type to the agent’s skills and priority_id to the agent’s priority_ids allowlist (empty / null = all priorities). Master has no agent_id pin. Received, Rejected, Calling, and reserved rows are skipped by auto-claim. Socket redial can reopen a settled row (call_type = redial; Settle & retry) with skill check only — no priority gate. Claim also waits on next_retry_at (Abandoned requires a due next_retry_at; other statuses allow null) and, only when sales_dialer_config.outbound_obey_work_hours = 1, refuses originate outside work_start–work_end (including due callbacks). With the default 0, auto-dial runs whenever a ready agent matches; next_retry_at is still clamped to the window (Wrap and working hours).
| Scenario | Pick? |
|---|---|
Skill matches, priority_ids empty | Yes (all priorities) |
Skill matches, lead priority_id in allowlist | Yes |
Skill matches, lead priority_id not in allowlist | No — next eligible lead / agent |
| Skill miss (any priority) | No |
| Predictive: idle union has one “all priorities” agent | Claim SQL skips priority filter; bridge still requires that agent’s skill+priority |
| Inbound hold → offer | Same skill + priority gate at pickSkilledAgent |
| Redial / ondemand | Skill only (no priority allowlist) |
Callback Request
priority_id 1 (Callback Schedule) honors callback_scheduled_at. Any idle agent with matching skill and priority allowlist may claim it when due. Full behaviour (time gate, 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.Any matching skill + priority agent may claim it.
Priority 1–3 without a datetime is Pending and dials like any other skill+priority-matched lead.
Hangup results
| Hangup | Stored status |
|---|---|
ANSWER | Received |
| Cancel by customer, invalid destination | Rejected |
| No answer, busy, congestion, chanunavail | Not Connected |
Received, Rejected, and Disposed are not claimed again (unless socket redial or a new CRM push). Not Connected uses sequential retry_pattern buckets (currently [4,4,3,2,1,1,1,1,1,1,1]) until max_tries (20). Leftover tries in the current bucket carry to the next working day; finishing a bucket waits until the next work_start before the next number. retry_seconds (3600) is the same-day gap for Not Connected. Abandoned is not on that cycle — it only waits retry_seconds when next_retry_at was empty; a slot after work_end snaps to the next working morning. call_count increments only on Not Connected. Auto-dial also stops when isMaintainance = 1.
Engine config
| Setting | Where | Meaning |
|---|---|---|
dialer_type | sales_dialer_config | Progressive or Predictive |
buffer_time | sales_dialer_config | Seconds between calls per agent |
max_tries | sales_dialer_config | Hard cap on Not Connected tries (currently 20) |
retry_seconds | sales_dialer_config | Same-day gap after Not Connected (currently 3600) |
retry_pattern | sales_dialer_config | Sequential buckets; leftover carries (currently [4,4,3,2,1,1,1,1,1,1,1]) |
work_start / work_end | sales_dialer_config | Retry window (currently 09:30–18:30 IST); also the originate window when outbound_obey_work_hours = 1 |
work_days | sales_dialer_config | ISO weekdays (currently [1,2,3,4,5,6,7]) |
outbound_obey_work_hours | sales_dialer_config | 1 = auto-dial only inside the work window. Default 0. Inbound and redial are unaffected |
disposed_priority_id | sales_dialer_config | Final Disposal — never claimed |
isMaintainance | sales_dialer_config | 1 stops new originates and redial |
MISSED_ACTIVITY_ENABLED / MISSED_ACTIVITY_GRACE_MINUTES | .env | Missed-activity outbound block on/off (default on) and grace minutes after a bridged call (default 10) |
AMI_AGENT_CHANNEL | .env | Default PJSIP/{extension} |
AMI_CALLERID | .env | Outbound CLI |
DIAL_ORIGINATE_TIMEOUT_MS | .env | Wait for AMI on_call (default 60s) |