Skip to content

Call Center (Predictive Dialer) ​

The Call Center app (slug call_center) is the outbound contact centre: a predictive dialer that re-derives its dial ratio every two seconds from live agent availability and measured call outcomes, an agent desk, and a supervisor console and wallboard around them. It is used by a sales or collections floor to work a list of numbers with as little agent idle time as the abandon-rate cap allows.

How it differs from the two things beside it ​

Three call-centre surfaces live on this platform and only one of them dials outbound at scale:

What it isDirection
FusionPBX call centre (v_call_center_*, via PBX (Admin))mod_callcenter queues — a caller waits, a strategy picks an agentInbound only
AI Call Center (convai)A voice AI replaces the agentOutbound, one line at a time per campaign
Call Center (this app)A floor of humans, paced predictivelyOutbound, many lines per agent

FusionPBX has no outbound side at all: no dialling list, no pacing, no retry cadence, no suppression list, no disposition codes. Those are exactly the seven tables this app adds.

What you can do ​

  • Run a campaign in one of four dial modes — preview (the agent clicks Dial), progressive (one line per free agent), power (a fixed ratio), or predictive (the ratio is derived from measurement and held under an abandon cap).
  • Watch the pacing, not just the result — the Dialer Console shows the current ratio, the measurements behind it, and the controller's own one-line explanation, over a live series of every decision it has made.
  • Take calls at the Agent Desk — go ready, receive bridged calls with the contact record and script in front of you, and close each one with a disposition that decides what happens to the contact next.
  • Simulate before dialling anybody — a new campaign starts in simulation mode, where the whole pacing loop runs against generated calls and optional virtual agents. Nothing reaches the trunk until simulation is switched off.
  • Build a list — paste names and numbers, or import CRM leads (only those with a usable phone; anything already on the do-not-call list is dropped).
  • Suppress a number — from the agent's disposition or directly; the entry also pulls the number out of every campaign that still holds it.
  • Watch the floor — the Wallboard is one tenant-wide read of every running campaign, every agent's state, and the day's call outcomes.

The pacing controller ​

app/apps/portal/services/call_center_pacing.py is a pure module with no I/O, so the model can be read and tested on its own. In outline:

  1. Warm-up. Under 25 completed calls the measured rates are noise, so the campaign paces progressively whatever mode it is in.
  2. The offered load. Erlang B gives the largest load (in erlangs) whose blocking probability stays inside the campaign's abandon cap. Blocking is the abandon rate here, because a connect that finds no free agent is dropped after max_wait_seconds. This is what makes the cap bind at the source rather than only after the fact — and it is why a six-seat floor sustains about 42% occupancy at a 3% cap while a hundred-seat floor sustains 92%.
  3. The lines that produce it. That load is a connect rate; divided by the chance a line reaches a live human (answer_rate × (1 − machine_rate)) and multiplied by how long a line stays occupied, it is a number of lines.
  4. The governor. A multiplicative controller on the measured abandon rate corrects the model's error — halving per minute over cap, creeping up 10% a minute under it — bounded to 0.35–1.25 so it corrects the model rather than replacing it.
  5. Ramp limiting on the way up only, and a progressive fallback at twice the cap. Progressive cannot abandon by construction, so the fallback is both safe and self-healing: the clean connects it makes are what bring the measured rate back down.

Every tick writes a CcPacingSample row holding the inputs, the decision and the reason, which is what the console charts and what makes an abandon rate answerable rather than merely reported.

How a call actually reaches an agent ​

Agents sit on a nailed-up line. Going ready originates their extension once with park_after_bridge=true and parks the answered channel; every customer is then bridged into it with uuid_bridge, which is instant. A predictive dialer cannot ring the agent after the customer answers — that is three to ten seconds of silence, and it is the dead-air complaint the whole technique is known for.

Customers are dialled as loopback/<number>/<domain>, which re-enters the org's own dialplan, so outbound routing, gateway selection, caller ID rules and CDR / billing are exactly what a call dialled from a handset would get. Nothing here writes to FusionPBX's tables.

Data model ​

  • CcCampaign (cc_campaigns) — the list's dial mode, pacing limits, calling window, retry cadence, caller ID and the dry_run switch.
  • CcContact (cc_contacts) — one row of a dialling list with its own attempt/retry state.
  • CcAgent (cc_agents) — an agent's live state (offline / ready / on_call / wrap / break), their nailed-up session and their heartbeat.
  • CcCall (cc_calls) — one dial attempt, including wait_ms (answer → bridge) and abandoned, the two numbers a predictive dialer is judged on.
  • CcDisposition (cc_dispositions) — an outcome code and what it does to the contact: close it, retry it on a timer, or suppress the number.
  • CcDncEntry (cc_dnc) — suppression, checked before every originate.
  • CcPacingSample (cc_pacing_samples) — one row per pacing tick.

Configuration & integrations ​

  • FreeSWITCH event socket — shared with the CRM click-to-dial; configured under App Settings → CRM, with the org's telephony.sip_domain as the fallback SIP domain (AI Call Center → Telephony Settings).
  • Agent extensions — read from the agent's own login (users.extension). An agent without one cannot go ready on a live campaign.
  • Answering-machine detection — amd_enabled drives mod_avmd, which is not present on every FreeSWITCH build (it is absent on the .62 box). It is off by default; the agent's "Voicemail" disposition feeds the same machine-rate input either way, and that input is what the pacing model actually consumes.
  • A restart drops every running campaign, deliberately. In-flight state is in memory; resurrecting a dialer across a deploy would originate calls nobody asked for from a process that has forgotten who was on the floor. The shutdown hook hangs up what is in the air rather than orphaning it.

API reference ​

All routes are under /call-center and are tenant-scoped; campaign mutations additionally require the admin role.

MethodPathPurpose
GET/POST/call-center/campaignsList / create campaigns
GET/PATCH/DELETE/call-center/campaigns/{id}Read, update or delete one
POST/call-center/campaigns/{id}/startStart the dialer loop (optionally with virtual agents, simulation only)
POST/call-center/campaigns/{id}/pause / /stopPause or stop it
GET/call-center/campaigns/{id}/pacingThe controller's decision series
POST/call-center/campaigns/{id}/contactsAdd contacts (structured or pasted)
POST/call-center/campaigns/{id}/import-leadsSeed the list from CRM leads
GET/call-center/agentThe agent desk's whole state in one poll
POST/call-center/agent/statusGo ready / on break / log out
POST/call-center/agent/heartbeatLiveness ping (the pacer's capacity check)
POST/call-center/agent/dispositionClose the current call
GET/call-center/wallboardTenant-wide live floor view
POST/call-center/dncSuppress a number and pull it from every list

Flat CRUD is on the generic grid engine at /grid/cc-campaigns, /grid/cc-contacts, /grid/cc-calls (read-only — a call row is evidence), /grid/cc-agents, /grid/cc-dispositions and /grid/cc-dnc. All six are admin_only, which is also what keeps their rows out of a non-admin agent's sidebar while leaving the Agent Desk reachable.

Lubb One Documentation