Shared behaviour
Settle, retry, wrap, pause, decline, redial.
These rules are the same in Progressive and Predictive. Hangup writes master + dialer_call_attempts (snapshot of master data_id at settle, for outgoing / incoming / redial). Only Not Connected increments call_count and uses retry_pattern + max_tries. Abandoned keeps an existing next_retry_at, or schedules retry_seconds when null — it is not on the NC retry cycle.
Hangup map
| AMI / condition | Stored status |
|---|---|
DialStatus = ANSWER (talked) | Received |
| Customer left on hold / connecting | Abandoned |
Cancel, DONTCALL, INVALIDARGS, causes 1, 21, 22, 27–29 | Rejected |
NOANSWER, BUSY, CONGESTION, CHANUNAVAIL, causes 16–20 | Not Connected |
Settle only updates rows still Calling. Agent, duration, hold seconds, and data_id are on the attempt row, not master. Attempt dial_status is this call’s outcome; previous_dial_status is master at claim.
Retry
| Not Connected | Abandoned | |
|---|---|---|
| Wait | retry_seconds (currently 3600) same day while the current pattern bucket is open, else next work_start | Keep existing next_retry_at, else retry_seconds (work-hours clamped). No pattern / max_tries |
| Pattern | retry_pattern buckets (currently [4,4,3,2,1,1,1,1,1,1,1]); leftover carries to the next working day | Ignored |
max_tries | Disposes when call_count hits the cap (currently 20) | Ignored |
call_count | Increments on this settle | Unchanged |
| Auto-claim | When next_retry_at is null or due | Only when next_retry_at is set and due |
Claim order is status tier first (Callback → Pending → Abandoned → Not Connected). Within Callback / Pending / Abandoned: priority_no, then oldest attempt. Within Not Connected: sooner next_retry_at, then Fresh Assign first, then lowest call_count. Abandoned is not on the NC pattern. Dispose exclusion uses only this module’s dialer_config (in_house_operations). Live knobs: retry_pattern = [4,4,3,2,1,1,1,1,1,1,1], retry_seconds = 3600, max_tries = 20, work_days = [1,2,3,4,5,6,7], window 09:30–18:30.
Claim only reserves the row (Calling, isReserved = 1, reserved_at). first_attempt_at / last_attempt_at are written right before a real outbound originate, or on an inbound claim. A claim released without dialing restores the previous status and waits RELEASE_RETRY_MS (default 60s; invalid numbers keep retry_seconds). Predictive claims only leads that one idle agent can take (skill and priority on the same agent). /api/health reports metrics.released, metrics.releasedNoAgent, leads.phantomAttempts (target 0) and leads.uncoveredLeads.
Wrap and working hours
buffer_time is per-agent wrap after a connected call. A miss with no lead retries claim every 10 seconds. Originate is refused when isMaintainance = 1 or when that agent has pending remarks. Auto-dial also needs an idle ready agent whose skills match the lead form_type.
dialer_config.outbound_obey_work_hours (default 0) controls whether outbound auto-dial (progressive and predictive) follows work_start–work_end on work_days:
| Flag | Outbound auto-dial | Inbound | Socket redial | Retry scheduling |
|---|---|---|---|---|
0 (default) | Any time a ready, matching agent exists | After-hours rules still apply | Never gated by hours | next_retry_at still clamped to the window |
1 | Only inside the work window (old behavior) | After-hours rules still apply | Never gated by hours | next_retry_at still clamped to the window |
Because retry scheduling stays clamped, after hours only leads whose next_retry_at is already due (or null) are claimed. A Not Connected at 18:45 still waits for the next work_start. Turn the old behavior back on with UPDATE dialer_config SET outbound_obey_work_hours = 1 WHERE module_id = 'in_house_operations';
Pause and decline
Panel pause writes dialer_employee.panel_status = pause, queue-pauses the extension, and opens a row on dialer_agent_pauses (source agent or auto on leave). Agent pause requires { reason } from public GET /api/pause-reasons. Pick ondemand for an on-demand OLI call. other requires a short note (12 words). Invalid or missing reason → server_error invalid_pause_reason and no pause. Ready closes that row and needs no reason. status / panel include pause_reason, pause_reason_note, and paused_at while paused. The dashboard Work column shows a live elapsed clock from that start time.
Daily day reset at PAUSE_DAY_RESET_AT (default 09:30 IST / DB_TIMEZONE) closes open pauses that started before today (resume_reason = day_reset) and sets panel_status = yet_to_login. The dialer ignores those desks until the agent goes Active. Same-morning pauses stay open. Kill switch: PAUSE_DAY_RESET_ENABLED=0.
SIP deny on a live predictive/inbound hold records dialer_agent_declines and blocks that extension for the same caller for decline_cooldown_seconds (default 30). The customer stays on hold. The same or another skilled agent is offered when the window ends.
Maintenance and pending remarks
dialer_config.isMaintainance (column spelling as stored) is the floor switch. 0 / null is fail-open (calls run). 1 stops new predictive/progressive originates, socket redial, and inbound park/offer (PSTN is left to FreePBX). Live talks are not hung. Dashboard and agent show a MAINTENANCE banner. Reloaded with the rest of config every 30 seconds.
Pending remarks: every ~30s the dialer POSTs agent_id to CRM getAllRemarksNotProvided. remarks true blocks auto-dial for that employee only (Dialer column Remarks). Inbound and redial stay allowed. Pause from the agent panel is refused until remarks are clear. CRM errors keep the last flag.
Redial
Socket redial with oli_id from a joined agent. Skips wrap and queue. Skill must match (not_owned otherwise). Blocked in maintenance (error: maintenance). Remarks do not block redial. ondemand: true allows redial from pause if SIP is idle (and unpauses on success). Reopens any active master row, including Received / Rejected / Disposed. Auto-dial attempts stay outgoing; inbound stays incoming; this attempt (and a decline on that predictive offer) is call_type = redial on dialer_call_attempts / dialer_agent_declines. Live dialer and call_connected also send that attempt call_type so the agent Type slot can show Outgoing / Incoming / Redial. status.call_type is still engine mode. A redial that ends Not Connected does not increment master call_count and does not send RNR mail (pattern / Final Disposal / RNR stay based on auto-dial NC only). Payload is in the Socket guide.
Outbound CRM posts
On every Not Connected (including the try that disposes), fire-and-forget:
- RNR mail — first time only while
isRnrSent = 0, then flag 1. Bodyagent_id"0" - NC retry schedule — if
next_retry_atis set. Body includesschedule_date - Hold Work Status — only after max-tries
Disposed. Bodyoli_idand a system remark signed withWS_SECRET
Logged on dialer_api_call_log. Failures never block settle. CRM POST /api/unholdDialerData is the inbound reverse of Hold Work Status. Details and curl for inbound HTTP are in the API reference.
Config that actually moves the needle
| Setting | Used? | Meaning |
|---|---|---|
dialer_type | Yes | Progressive or Predictive |
buffer_time | Yes | Wrap seconds |
predictive_ratio | Yes | Customer legs per idle agent |
decline_cooldown_seconds | Yes | Decline block on the same hold |
max_lines | Yes | Cap ringing + hold + connecting |
max_tries / retry_pattern | Yes | Not Connected only (live: 20 tries, pattern [4,4,3,2,1,…]) |
retry_seconds | Yes | NC same-day gap and Abandoned wait (live: 3600) |
disposed_priority_id | Yes | Final Disposal — never auto-claimed |
isMaintainance | Yes | 1 pauses new floor calls; live calls stay up |
hold_timeout_seconds | No | Unused leftover |
max_abandon_rate | No | Unused leftover |