Dialer docsDispositions
Home Progressive Predictive Callback Disposition API Socket Dashboard

Agent dispositions

CRM wrap-up updates dialer master and writes student-wise history.

When an agent picks Not Interested, Invalid, DND, Language Barrier, or Call Not Connected on Auto Dialer (table-v2), CRM saves marketing status and notifies nodeDialerFIC. Dispose statuses leave the pool. Call Not Connected schedules the next retry with the same policy as AMI no-answer. Every wrap-up is stored in fic_dialer_disposition_history for later reports. Field-level curl lives in the API reference.

POST /api/dispositions Dispose 4 / 7 / 8 / 9 Retry 6 History ≠ call_attempts

End-to-end flow

1
Agent picks Disposition
Auto Dialer card dropdown (hardcoded): NI / Invalid / DND / Language Barrier / Call Not Connected. Modal loads sub-options from CRM lead_tag_status_new.
2
CRM ChangeWorkStatus
DialerController/ChangeWorkStatus → UpdateWorkStatus writes CRM tables (student_registration.lead_status_val, marketing_data, tag history).
3
CRM → dialer notify
DialerModel::push_disposition_to_dialer POSTs JSON to https://dialer…/api/dispositions with Bearer WS_SECRET. Uses FIC id (new_std_id), not CRM numeric std_id.
4
Dialer applies action
Dispose → master Disposed + Final Disposal priority. CNC → Not Connected + next_retry_at (or dispose at max tries). Always inserts history.
5
Panel alert
JSON response: CRM saved + dialer ok/error. If dialer URL is wrong you see the real error (not a silent CRM-only success).

Status mapping

Dropdown values are CRM lead_status_val / tag_id codes. Dialer maps only these five for master updates:

crm_lead_statusLabelDialer actionMaster result
4Not Interesteddisposedial_status=Disposed, priority_id=disposed_priority_id, clear retry & callback
8Invaliddisposesame
7DNDdisposesame
9Language Barrierdisposesame (+ CRM may update preferred language from sub-disposition)
6Call Not Connectedretrydial_status=Not Connected + next_retry_at via retry policy; dispose if max_tries reached

Sub-disposition options (Status Remark modal) come from CRM table lead_tag_status_new where tag_id = the main status. They are stored on the history row as disposition_status / disposition_label; they do not change the dispose vs retry decision.

Schedule Callback modal statuses Payment (10) / Call Back (12) use /api/callbacks, not this disposition API.

Master updates

Table: fic_dialer_master_data, keyed by student_id (FIC / new_std_id).

ActionColumns touched
Disposedial_status='Disposed', priority_id → Final Disposal, next_retry_at=NULL, callback_scheduled_at=NULL, isReserved=0
Retry (CNC)dial_status='Not Connected', next_retry_at from 2-2-1 / working-hours policy, isReserved=0
CNC at max triesSame as dispose

Unlike hangup settle(), CRM dispose/retry does not require dial_status='Calling'. It works after wrap-up when the row is already Received / Not Connected / etc.

If the FIC id is not in master, history is still written (lead_found: false, master_updated: false). The engine will not dial disposed or Final Disposal rows.

History table

fic_dialer_disposition_history — migration 002_fic_dialer_disposition_history.sql.

ColumnMeaning
student_idFIC id in dialer master
crm_std_idCRM student_registration.std_id
crm_lead_status / _labelMain disposition (4/6/7/8/9)
disposition_status / _labelSub-disposition from lead_tag_status_new
remark, tag_from, agent_idAgent wrap-up context
actiondispose or retry
dial_status_before / _afterMaster snapshot around the update
next_retry_atSet on CNC retry; null on dispose
sourceUsually crm
created_atWhen wrap-up was recorded

vs call_attempts

fic_dialer_call_attemptsfic_dialer_disposition_history
Written byAMI hangup settleCRM / API wrap-up
MeaningTelephony try (answer, reject, NC, abandon)Agent CRM disposition + remark
Use forDial counts, 2-2-1 daily cap, connect rateDisposition reports, NI/DND rates, CNC tags

Do not mix them: an answered call can later be tagged Not Interested in CRM. That is one attempt row (Received) plus one history row (dispose).

HTTP API

POST /api/dispositions — auth same as leads/callbacks (Authorization: Bearer WS_SECRET).

curl -sS -X POST https://dialer.fastinfoclass.com/api/dispositions \
  -H "Authorization: Bearer YOUR_WS_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "student_id": "FIC178538363527762",
    "crm_std_id": 12345,
    "crm_lead_status": 4,
    "crm_lead_status_label": "Not Interested",
    "disposition_status": 12,
    "disposition_label": "Already enrolled",
    "remark": "Not interested in course",
    "tag_from": "fresh_tag",
    "agent_id": 42,
    "source": "crm"
  }'
FieldRequiredNotes
student_idYesFIC / master key
crm_lead_statusYes4, 6, 7, 8, or 9 only
crm_std_idNoCRM numeric id for reports
disposition_statusNoSub-disposition id
remark, tag_from, agent_idNoStored on history
sourceNoDefault crm

Success shape includes action, dial_status_before/after, next_retry_at, master_updated, lead_found, history_id.

CRM wiring

PieceRole
table-v2.phpAuto Dialer UI. Disposition dropdown; submits to DialerController/ChangeWorkStatus with id (std_id) + fic_id
DialerController.phpSession auth; builds payload; returns JSON { ok, crm, dialer }
DialerModel.phpchange_work_status → CRM update then push_disposition_to_dialer
Login_database::UpdateWorkStatusCRM DB writes (unchanged contract)
application/config/config.phpfic_dialer_api_base, fic_dialer_api_token

Initial dropdown options are hardcoded in the view (same as AllData). They are not loaded from a dialer table. Sub-dispositions load via DialerController/tagDispositionStatus_new → lead_tag_status_new.

CRM config (critical)

On the live marketing server, in the exact application/config/config.php that CodeIgniter loads:

$config['fic_dialer_api_base'] = 'https://dialer.fastinfoclass.com';
$config['fic_dialer_api_token'] = 'SAME_AS_DIALER_WS_SECRET';

If fic_dialer_api_base is missing, older DialerModel builds fell back to http://127.0.0.1:8000 — Postman on your laptop still worked, panel did not. Current DialerModel defaults to the public dialer host instead of localhost. Still set config correctly and reload PHP-FPM after upload.

FileZilla temp under /tmp/fz3temp-2/ is only a download copy. Live PHP reads the file under the webroot (e.g. /var/www/html/marketting/application/…).

Deploy

Dialer host

cd /opt/fic-dialer   # or your install path
git pull origin main
npm ci --omit=dev
npm run migrate      # applies 002_fic_dialer_disposition_history.sql
pm2 restart fic-dialer

Marketing / CRM host

Upload / sync at least:

sudo systemctl reload php-fpm   # or httpd / apache2
grep -n fic_dialer_api application/config/config.php

Verify

CheckExpected
POST /api/dispositions without auth401 unauthorized (route exists; not 404)
Postman with Bearer + valid FIC idok: true, new history row, master updated
Panel after dispositionAlert: “Dialer updated” (not localhost URL)
SQL historyRow in fic_dialer_disposition_history for that student_id
SQL master (dispose)dial_status='Disposed', Final Disposal priority
SQL master (CNC)dial_status='Not Connected', next_retry_at set

Report queries

-- Latest wrap-ups for one student
SELECT student_id, crm_std_id, crm_lead_status_label, action,
       disposition_label, remark, agent_id, next_retry_at, created_at
FROM fic_dialer_disposition_history
WHERE student_id = 'FIC178538363527762'
ORDER BY created_at DESC;

-- Counts by disposition today
SELECT crm_lead_status, crm_lead_status_label, action, COUNT(*) AS n
FROM fic_dialer_disposition_history
WHERE created_at >= CURDATE()
GROUP BY crm_lead_status, crm_lead_status_label, action
ORDER BY n DESC;

-- Join telephony attempts (optional)
SELECT h.created_at AS tagged_at, h.crm_lead_status_label, h.action,
       a.tried_at, a.dial_status AS attempt_status, a.duration_seconds
FROM fic_dialer_disposition_history h
LEFT JOIN fic_dialer_call_attempts a
  ON a.student_id = h.student_id
 AND a.tried_at <= h.created_at
WHERE h.student_id = 'FIC178538363527762'
ORDER BY h.created_at DESC, a.tried_at DESC;

Troubleshooting

SymptomCause / fix
Panel success, empty history; Postman worksCRM not calling dialer, or wrong URL. Redeploy DialerModel + config. Panel alert now shows dialer error.
Error URL http://127.0.0.1:8000/…fic_dialer_api_base empty on live. Upload config.php; reload PHP; use DialerModel with public fallback.
401 unauthorized from CRMfic_dialer_api_token ≠ dialer WS_SECRET
404 on /api/dispositionsOld dialer build — git pull, restart PM2
ER_NO_SUCH_TABLE disposition historynpm run migrate on dialer host
History written, master not disposedlead_found: false — FIC id not in fic_dialer_master_data
Master status=0Inactive for claim; dispose/history can still write. Reactivate only if product requires redial.
Alert “No dialer response”Live DialerModel still old (returns plain true). Redeploy model + controller + table-v2

Code map (dialer): src/http/routes/disposition.routes.js → controller → disposition.service.js → lead.repository dispose/retry + disposition.repository insert. CRM map: DialerModel::push_disposition_to_dialer.