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.
End-to-end flow
Auto Dialer card dropdown (hardcoded): NI / Invalid / DND / Language Barrier / Call Not Connected. Modal loads sub-options from CRM
lead_tag_status_new.DialerController/ChangeWorkStatus → UpdateWorkStatus writes CRM tables (student_registration.lead_status_val, marketing_data, tag history).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.Dispose → master
Disposed + Final Disposal priority. CNC → Not Connected + next_retry_at (or dispose at max tries). Always inserts history.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_status | Label | Dialer action | Master result |
|---|---|---|---|
4 | Not Interested | dispose | dial_status=Disposed, priority_id=disposed_priority_id, clear retry & callback |
8 | Invalid | dispose | same |
7 | DND | dispose | same |
9 | Language Barrier | dispose | same (+ CRM may update preferred language from sub-disposition) |
6 | Call Not Connected | retry | dial_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).
| Action | Columns touched |
|---|---|
| Dispose | dial_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 tries | Same 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.
| Column | Meaning |
|---|---|
student_id | FIC id in dialer master |
crm_std_id | CRM student_registration.std_id |
crm_lead_status / _label | Main disposition (4/6/7/8/9) |
disposition_status / _label | Sub-disposition from lead_tag_status_new |
remark, tag_from, agent_id | Agent wrap-up context |
action | dispose or retry |
dial_status_before / _after | Master snapshot around the update |
next_retry_at | Set on CNC retry; null on dispose |
source | Usually crm |
created_at | When wrap-up was recorded |
vs call_attempts
fic_dialer_call_attempts | fic_dialer_disposition_history | |
|---|---|---|
| Written by | AMI hangup settle | CRM / API wrap-up |
| Meaning | Telephony try (answer, reject, NC, abandon) | Agent CRM disposition + remark |
| Use for | Dial counts, 2-2-1 daily cap, connect rate | Disposition 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"
}'
| Field | Required | Notes |
|---|---|---|
student_id | Yes | FIC / master key |
crm_lead_status | Yes | 4, 6, 7, 8, or 9 only |
crm_std_id | No | CRM numeric id for reports |
disposition_status | No | Sub-disposition id |
remark, tag_from, agent_id | No | Stored on history |
source | No | Default crm |
Success shape includes action, dial_status_before/after, next_retry_at, master_updated, lead_found, history_id.
CRM wiring
| Piece | Role |
|---|---|
table-v2.php | Auto Dialer UI. Disposition dropdown; submits to DialerController/ChangeWorkStatus with id (std_id) + fic_id |
DialerController.php | Session auth; builds payload; returns JSON { ok, crm, dialer } |
DialerModel.php | change_work_status → CRM update then push_disposition_to_dialer |
Login_database::UpdateWorkStatus | CRM DB writes (unchanged contract) |
application/config/config.php | fic_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:
application/models/DialerModel.phpapplication/controllers/DialerController.php- Auto Dialer view
table-v2.php(path as deployed) application/config/config.phpwithfic_dialer_api_*
sudo systemctl reload php-fpm # or httpd / apache2 grep -n fic_dialer_api application/config/config.php
Verify
| Check | Expected |
|---|---|
POST /api/dispositions without auth | 401 unauthorized (route exists; not 404) |
| Postman with Bearer + valid FIC id | ok: true, new history row, master updated |
| Panel after disposition | Alert: “Dialer updated” (not localhost URL) |
| SQL history | Row 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
| Symptom | Cause / fix |
|---|---|
| Panel success, empty history; Postman works | CRM 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 CRM | fic_dialer_api_token ≠ dialer WS_SECRET |
404 on /api/dispositions | Old dialer build — git pull, restart PM2 |
ER_NO_SUCH_TABLE disposition history | npm run migrate on dialer host |
| History written, master not disposed | lead_found: false — FIC id not in fic_dialer_master_data |
Master status=0 | Inactive 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.