Callback Request
One agent. One time. Nobody else dials it.
A callback is not a pool lead. CRM sends callback_scheduled_at and agent_id together. The dialer waits until that time, then originates only on that employee’s extension. Field shapes are in the API reference.
End-to-end flow
The row sits as Callback with agent_id already set. Claim starts only when the time is due and that agent is panel-active, ready, and AMI idle.
Push
callback_scheduled_at is only for priority_id 1, 2, or 3 and requires agent_id. agent_id alone is allowed on any lead and pins that employee’s extension. agent_id is dialer_employee.id (not the SIP extension) and must have an extension on the dialer_employee roster. Tickets already on priority_id 8 use PUT /api/escalated-tickets to Close or Callback Schedule — that path does not go through this push body.
| Body | Result |
|---|---|
Time + agent_id | Stored. priority_id 1, 2, or 3 → dial_status = Callback. |
Time, no agent_id | 400 agent_id is required |
agent_id, no time | Stored as Pending. Only that agent is called (any priority). |
| Unknown id / no extension | 400 |
Accepted times: 2026-08-20 15:30:00 or ISO-8601, including past datetimes (due immediately). Rejected: date-only. Other priority_id values cannot send a time.
curl -sS -X POST https://YOUR_HOST/api/leads \
-H "Authorization: Bearer YOUR_WS_SECRET" \
-H "Content-Type: application/json" \
-d '{
"oli_id": "OLI124",
"phone_number": "9876543211",
"client_name": "B",
"form_type": "GST",
"priority_id": 3,
"callback_scheduled_at": "2026-08-20 15:30:00",
"agent_id": 42
}'
Wait until due
Both engines skip a lead while callback_scheduled_at is in the future. Until then the row stays unreserved. When the time is null or <= NOW(), it can be claimed. A due callback skips the working-hours gate, so a 20:00 CRM time is originated even if work_end is 18:30. After a later Not Connected, the 2-2-1 scheduler resumes.
Only that agent
Claim requires
agent_id = that agent’s employee.id.form_type does not need to be in their skills. The customer asked for this person.Panel active, presence ready, AMI idle, wrap finished. If they are paused or on a call, the lead waits. Nobody else takes it.
Progressive vs Predictive
| Progressive | Predictive | |
|---|---|---|
| When claimed | First in that agent’s claimNextLead queue | When that agent is idle, via claimAssignedLead |
| Overdial pool | Not shared; other agents cannot take it | Never in the pool. Not originated for a random skilled agent |
| Originate | call.php to that extension | Customer first, then Bridge only to that extension |
| If agent busy after answer | N/A (agent-first) | Hold until that agent is free, or abandon on hold timeout |
Hangup and retry
| Outcome | Status | agent_id |
|---|---|---|
Talked (ANSWER) | Received | Kept |
| No answer / busy / congestion | Not Connected | Kept — 2-2-1 + working hours |
call_count reaches max_tries | Disposed | Kept — Final Disposal, never dialed |
| Reject / customer cancel | Rejected | Kept |
| Hold timeout (predictive) | Abandoned | Kept — same scheduler as Not Connected |
Failed originate restores the previous status, clears reserve, and keeps the pin. Received / Rejected / Disposed are not claimed. Pinned callbacks use the same retry policy as pool leads: retry_pattern default [2,2,1], same-day retry_seconds, window work_start–work_end. A due callback_scheduled_at is originated even after hours; a later Not Connected snaps to the next working morning. Dispose only when call_count reaches max_tries. First Not Connected also sends RNR mail if isRnrSent is still 0.