Callcenter Direct API
The Callcenter Direct API provides comprehensive management of agents, tiers (agent-to-queue assignments), and members (callers in queue). This API uses database-backed storage for all operations, enabling real-time state management with ~100ms polling by FreeSWITCH's mod_callcenter.
Architecture Overview
Key Components
- Database-Backed Operations: All agent, tier, and member operations write directly to PostgreSQL
- Real-Time Sync:
mod_callcenterpolls the database every ~100ms for changes - XML-RPC for Lifecycle: Queue load/unload/reload operations use XML-RPC commands
- Hybrid Architecture: Combines database writes with XML-RPC for maximum control
Agent Management
Agents are the workforce of your call center. They handle incoming calls from queues and can be in various states and statuses.
Agent States vs Status
Understanding the difference between state and status is critical:
| Status | Description | Controlled By |
|---|---|---|
Logged Out | Agent is not active | Agent/Supervisor |
Available | Agent is ready to receive calls | Agent/Supervisor |
Available (On Demand) | Agent accepts calls manually | Agent/Supervisor |
On Break | Agent is on a scheduled break | Agent/Supervisor |
Unknown | Initial/error state | System |
| State | Description | Controlled By |
|---|---|---|
Waiting | Agent is idle, waiting for a call | System |
Receiving | Agent is being offered a call | System |
In a queue call | Agent is actively on a call | System |
Idle | Agent is between calls (wrap-up time) | System |
Reserved | Agent is reserved for a specific call | System |
Unknown | Initial/error state | System |
Rule of Thumb:
- Status = What the agent wants to do (set by humans)
- State = What the agent is currently doing (set by FreeSWITCH)
Agent State Machine
Agent Lifecycle Flow
Create Agent
Create a new agent in the database. The agent will be created with status='Logged Out' and state='Waiting'.
curl -X POST https://api.example.com/v1/callcenter/agents \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "agent_001",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-001",
"max_no_answer": 3,
"wrap_up_time": 10,
"reject_delay_time": 10,
"busy_delay_time": 60,
"no_answer_delay_time": 10
}'
Response:
{
"message": "Agent agent_001 created successfully",
"agent": {
"name": "agent_001",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-001",
"status": "Logged Out",
"state": "Waiting",
"max_no_answer": 3,
"wrap_up_time": 10,
"reject_delay_time": 10,
"busy_delay_time": 60,
"no_answer_delay_time": 10,
"instance_id": "single_box",
"uuid": "",
"last_bridge_start": 0,
"last_bridge_end": 0,
"last_offered_call": 0,
"last_status_change": 1640995200,
"no_answer_count": 0,
"calls_answered": 0,
"talk_time": 0,
"ready_time": 0
}
}
Agent Contact Formats
The contact field determines how FreeSWITCH reaches the agent:
| Format | Use Case | Example |
|---|---|---|
sofia/gateway/registrar/agent-{id} | Recommended: Routes through registrar B2BUA for NAT handling | sofia/gateway/registrar/agent-001 |
user/{extension}@{domain} | Direct SIP user lookup | user/1001@phone.example.com |
sofia/internal/{extension}@{domain} | Direct Sofia profile routing | sofia/internal/1001@192.168.1.100 |
Use sofia/gateway/registrar/agent-{id} to route agent calls through the registrar pod. This provides:
- NAT traversal for external SIP clients
- Media anchoring between internal pods and external agents
- Public IP advertisement for RTP streams
Agent Configuration Parameters
| Parameter | Description | Default | Range |
|---|---|---|---|
max_no_answer | Maximum no-answer attempts before agent marked unavailable | 3 | 0-100 |
wrap_up_time | Seconds after call ends before agent is available again | 10 | 0-3600 |
reject_delay_time | Delay before offering call to agent who rejected previous call | 10 | 0-3600 |
busy_delay_time | Delay before offering call to agent who was busy | 60 | 0-3600 |
no_answer_delay_time | Delay before offering call to agent who didn't answer | 10 | 0-3600 |
Update Agent Status
Update an agent's status to control their availability.
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_001/status \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"status": "Available"
}'
Valid Status Values:
Available- Agent ready to receive callsAvailable (On Demand)- Agent accepts calls manually (not implemented in UI)On Break- Agent temporarily unavailableLogged Out- Agent not active
Update Agent Configuration
Update agent configuration without changing status.
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_001 \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"contact": "sofia/gateway/registrar/agent-001",
"wrap_up_time": 15,
"max_no_answer": 5
}'
List Agents
List all agents with optional filtering.
# List all agents
curl https://api.example.com/v1/callcenter/agents \
-H "X-API-Key: your-api-key"
# Filter by queue
curl https://api.example.com/v1/callcenter/agents?queue=support_queue \
-H "X-API-Key: your-api-key"
# Filter by status
curl https://api.example.com/v1/callcenter/agents?status=Available \
-H "X-API-Key: your-api-key"
# Filter by state
curl https://api.example.com/v1/callcenter/agents?state=Waiting \
-H "X-API-Key: your-api-key"
Response:
{
"agents": [
{
"name": "agent_001",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-001",
"status": "Available",
"state": "Waiting",
"max_no_answer": 3,
"wrap_up_time": 10,
"no_answer_count": 0,
"calls_answered": 5,
"talk_time": 300
}
],
"total": 1,
"filters": {
"queue": null,
"status": null,
"state": null
}
}
Get Agent Details
Retrieve detailed information for a specific agent.
curl https://api.example.com/v1/callcenter/agents/agent_001 \
-H "X-API-Key: your-api-key"
Delete Agent
Delete an agent and cascade delete all tier assignments.
curl -X DELETE https://api.example.com/v1/callcenter/agents/agent_001 \
-H "X-API-Key: your-api-key"
Deleting an agent will also delete all tier assignments (agent-to-queue mappings) for that agent.
Tier Management
Tiers determine which agents can handle calls for which queues, and in what priority order.
Tier Concepts
Call Distribution with Tiers
Tier Properties
| Property | Description | Range |
|---|---|---|
level | Priority tier (lower = higher priority) | 1-100 |
position | Position within tier (affects ring order) | 1-100 |
state | Current tier state | See states below |
Tier States
| State | Description |
|---|---|
Ready | Tier is active and can receive calls |
Standby | Tier is temporarily inactive |
No Answer | Last call attempt resulted in no answer |
Offering | Call is currently being offered via this tier |
Active Inbound | Agent is on an inbound call via this tier |
Create Tier
Assign an agent to a queue with a specific priority level and position.
curl -X POST https://api.example.com/v1/callcenter/tiers \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"queue": "support_queue",
"agent": "agent_001",
"level": 1,
"position": 1,
"state": "Ready"
}'
Response:
{
"message": "Tier created successfully",
"tier": {
"queue": "support_queue",
"agent": "agent_001",
"level": 1,
"position": 1,
"state": "Ready"
}
}
Update Tier
Update tier level, position, or state.
curl -X PUT https://api.example.com/v1/callcenter/tiers/support_queue/agent_001 \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"level": 2,
"position": 3,
"state": "Ready"
}'
List Tiers
List all tier assignments with optional filtering.
# List all tiers
curl https://api.example.com/v1/callcenter/tiers \
-H "X-API-Key: your-api-key"
# Filter by queue
curl https://api.example.com/v1/callcenter/tiers?queue=support_queue \
-H "X-API-Key: your-api-key"
# Filter by agent
curl https://api.example.com/v1/callcenter/tiers?agent=agent_001 \
-H "X-API-Key: your-api-key"
Response:
{
"tiers": [
{
"queue": "support_queue",
"agent": "agent_001",
"level": 1,
"position": 1,
"state": "Ready"
},
{
"queue": "support_queue",
"agent": "agent_002",
"level": 1,
"position": 2,
"state": "Ready"
},
{
"queue": "sales_queue",
"agent": "agent_001",
"level": 2,
"position": 1,
"state": "Standby"
}
],
"total": 3,
"filters": {
"queue": null,
"agent": null
}
}
Get Tier
Retrieve a specific tier assignment.
curl https://api.example.com/v1/callcenter/tiers/support_queue/agent_001 \
-H "X-API-Key: your-api-key"
Delete Tier
Remove an agent from a queue.
curl -X DELETE https://api.example.com/v1/callcenter/tiers/support_queue/agent_001 \
-H "X-API-Key: your-api-key"
Member Management
Members are callers currently in queue waiting for an agent. Members are managed automatically by mod_callcenter during active calls. These APIs provide read-only access to member data.
Member Lifecycle
Member States
| State | Description |
|---|---|
Waiting | Member is in queue, waiting for an agent |
Trying | Call is being offered to an agent |
Answered | Agent has answered, call is connected |
Abandoned | Caller hung up before being answered |
List Members
List all members (callers in queue) with optional filtering.
# List all members
curl https://api.example.com/v1/callcenter/members \
-H "X-API-Key: your-api-key"
# Filter by queue
curl https://api.example.com/v1/callcenter/members?queue=support_queue \
-H "X-API-Key: your-api-key"
# Filter by state
curl https://api.example.com/v1/callcenter/members?state=Waiting \
-H "X-API-Key: your-api-key"
Response:
{
"members": [
{
"uuid": "12345678-1234-1234-1234-123456789012",
"queue": "support_queue",
"session_uuid": "87654321-4321-4321-4321-210987654321",
"cid_number": "+1234567890",
"cid_name": "John Doe",
"system_epoch": 1640995200,
"joined_epoch": 1640995200,
"base_score": 0,
"skill_score": 0,
"state": "Waiting",
"serving_agent": null,
"instance_id": "single_box"
}
],
"total": 1,
"filters": {
"queue": "support_queue",
"state": null
}
}
Get Member
Retrieve details for a specific member by UUID.
curl https://api.example.com/v1/callcenter/members/12345678-1234-1234-1234-123456789012 \
-H "X-API-Key: your-api-key"
Member APIs are read-only. Members are created and managed automatically by mod_callcenter during call flows. You cannot manually create or delete members via the API.
Queue Lifecycle Operations
The Callcenter Direct API also provides XML-RPC-based queue lifecycle operations.
Load Queue
Tell FreeSWITCH to start accepting calls for a queue.
curl -X POST https://api.example.com/v1/callcenter/queues/support_queue/load \
-H "X-API-Key: your-api-key"
Unload Queue
Tell FreeSWITCH to stop accepting calls for a queue.
curl -X POST https://api.example.com/v1/callcenter/queues/support_queue/unload \
-H "X-API-Key: your-api-key"
Reload Queue
Reload queue configuration from the database.
curl -X POST https://api.example.com/v1/callcenter/queues/support_queue/reload \
-H "X-API-Key: your-api-key"
Get Queue Status
Get comprehensive real-time queue status.
curl https://api.example.com/v1/callcenter/queues/support_queue/status \
-H "X-API-Key: your-api-key"
Response:
{
"queue": "support_queue",
"status": {
"queue": "name=support_queue|status=active",
"agents": "agent_001|Available|Waiting|sofia/gateway/registrar/agent-001\nagent_002|On Break|Idle|sofia/gateway/registrar/agent-002",
"members": "12345678-1234-1234-1234-123456789012|null|Waiting|+1234567890"
}
}
Break Types and Tracking
Agents can be set to "On Break" status to indicate temporary unavailability.
Setting Agent on Break
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_001/status \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"status": "On Break"
}'
Break Tracking
The system tracks break times via:
last_status_changetimestamp in the agents tableready_timefield tracks cumulative time in Available status- External systems can calculate break duration by monitoring status changes
While the core API supports "On Break" status, break type categorization (lunch, training, etc.) should be implemented in your application layer by tracking status changes with metadata in a separate table.
Real-Time Status Queries
Agent Statistics
Each agent tracks comprehensive statistics:
| Field | Description |
|---|---|
calls_answered | Total calls answered |
no_answer_count | Number of no-answer events |
talk_time | Total time in calls (seconds) |
ready_time | Total time in Available status (seconds) |
last_bridge_start | Timestamp of last call start |
last_bridge_end | Timestamp of last call end |
last_offered_call | Timestamp of last call offer |
last_status_change | Timestamp of last status change |
Queue Statistics
Get member counts by state:
# Via database adapter (internal use)
# Returns: { count, waiting, trying, answered, abandoned }
All read operations query the database directly for real-time accuracy. For high-frequency status polling, consider implementing caching in your application layer.
Error Handling
The API returns structured errors with clear error codes:
Error Response Format
{
"code": "AGENT_NOT_FOUND",
"message": "Agent agent_001 not found",
"details": {
"agent_name": "agent_001"
}
}
Common Error Codes
| Code | Status | Description |
|---|---|---|
AGENT_ALREADY_EXISTS | 409 | Agent with this name already exists |
AGENT_NOT_FOUND | 404 | Agent not found |
INVALID_STATUS | 400 | Invalid agent status value |
TIER_ALREADY_EXISTS | 409 | Tier assignment already exists |
TIER_NOT_FOUND | 404 | Tier assignment not found |
MEMBER_NOT_FOUND | 404 | Member (caller) not found |
CREATE_AGENT_ERROR | 500 | Failed to create agent |
UPDATE_STATUS_ERROR | 500 | Failed to update agent status |
Database Schema
The callcenter system uses three main tables:
-- Agents table
CREATE TABLE agents (
name VARCHAR(255) PRIMARY KEY,
instance_id VARCHAR(255) NOT NULL DEFAULT 'single_box',
uuid VARCHAR(255) NOT NULL DEFAULT '',
type VARCHAR(50) NOT NULL,
contact VARCHAR(255) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'Logged Out',
state VARCHAR(50) NOT NULL DEFAULT 'Waiting',
max_no_answer INTEGER NOT NULL DEFAULT 3,
wrap_up_time INTEGER NOT NULL DEFAULT 10,
reject_delay_time INTEGER NOT NULL DEFAULT 10,
busy_delay_time INTEGER NOT NULL DEFAULT 60,
no_answer_delay_time INTEGER NOT NULL DEFAULT 10,
last_bridge_start INTEGER NOT NULL DEFAULT 0,
last_bridge_end INTEGER NOT NULL DEFAULT 0,
last_offered_call INTEGER NOT NULL DEFAULT 0,
last_status_change INTEGER NOT NULL DEFAULT 0,
no_answer_count INTEGER NOT NULL DEFAULT 0,
calls_answered INTEGER NOT NULL DEFAULT 0,
talk_time INTEGER NOT NULL DEFAULT 0,
ready_time INTEGER NOT NULL DEFAULT 0,
external_calls_count INTEGER NOT NULL DEFAULT 0
);
-- Tiers table
CREATE TABLE tiers (
queue VARCHAR(255) NOT NULL,
agent VARCHAR(255) NOT NULL,
state VARCHAR(50) NOT NULL DEFAULT 'Ready',
level INTEGER NOT NULL DEFAULT 1,
position INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (queue, agent),
FOREIGN KEY (agent) REFERENCES agents(name) ON DELETE CASCADE
);
-- Members table (managed by mod_callcenter)
CREATE TABLE members (
uuid VARCHAR(255) PRIMARY KEY,
instance_id VARCHAR(255) NOT NULL DEFAULT 'single_box',
session_uuid VARCHAR(255) NOT NULL,
queue VARCHAR(255) NOT NULL,
system_epoch INTEGER NOT NULL,
joined_epoch INTEGER NOT NULL,
rejoined_epoch INTEGER NOT NULL DEFAULT 0,
bridge_epoch INTEGER NOT NULL DEFAULT 0,
abandoned_epoch INTEGER NOT NULL DEFAULT 0,
base_score INTEGER NOT NULL DEFAULT 0,
skill_score INTEGER NOT NULL DEFAULT 0,
cid_number VARCHAR(255),
cid_name VARCHAR(255),
serving_agent VARCHAR(255),
serving_system VARCHAR(255),
state VARCHAR(50) NOT NULL DEFAULT 'Waiting'
);
All tables use instance_id = 'single_box' to scope data to the current FreeSWITCH cluster. This supports future multi-cluster deployments.
Architecture Decision: Database vs XML-RPC
ADR-005: Database-Backed State Management
Status: Accepted
Context:
FreeSWITCH mod_callcenter traditionally uses XML-RPC commands for all operations. However, this creates challenges:
- No persistent state (data lost on restart)
- No audit trail
- Limited query capabilities
- Race conditions with concurrent updates
Decision:
Use PostgreSQL as the source of truth for agents, tiers, and members. mod_callcenter polls the database every ~100ms for changes.
Consequences:
✅ Advantages:
- Persistent state across restarts
- Full audit trail and history
- Rich querying (JOIN, filter, aggregate)
- ACID transactions for consistency
- Easy backup and replication
❌ Trade-offs:
- ~100ms latency for state changes to take effect
- Database is a critical dependency
- Need to manage database connections
Implementation:
- All CRUD operations write to PostgreSQL
mod_callcenterusesodbc-dsnconfig to poll database- XML-RPC used only for queue lifecycle (load/unload/reload)
Comparison: Database vs XML-RPC
| Operation | Database Approach | XML-RPC Approach |
|---|---|---|
| Agent Create | INSERT into agents table | callcenter_config agent add ... |
| Agent Status | UPDATE agents SET status=... | callcenter_config agent set status ... |
| Tier Create | INSERT into tiers table | callcenter_config tier add ... |
| State Persistence | ✅ Persistent | ❌ Memory only |
| Audit Trail | ✅ Full history | ❌ No history |
| Query Flexibility | ✅ SQL queries | ❌ Limited |
| Latency | ~100ms poll time | Immediate |
| Concurrency | ✅ ACID transactions | ⚠️ Race conditions |
Complete Workflow Example
Here's a complete example of setting up agents and handling calls:
# 1. Create agents
curl -X POST https://api.example.com/v1/callcenter/agents \
-H "X-API-Key: your-api-key" \
-d '{
"name": "agent_001",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-001",
"max_no_answer": 3,
"wrap_up_time": 15
}'
curl -X POST https://api.example.com/v1/callcenter/agents \
-H "X-API-Key: your-api-key" \
-d '{
"name": "agent_002",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-002",
"max_no_answer": 3,
"wrap_up_time": 15
}'
# 2. Assign agents to queue (Tier 1, different positions)
curl -X POST https://api.example.com/v1/callcenter/tiers \
-H "X-API-Key: your-api-key" \
-d '{
"queue": "support_queue",
"agent": "agent_001",
"level": 1,
"position": 1
}'
curl -X POST https://api.example.com/v1/callcenter/tiers \
-H "X-API-Key: your-api-key" \
-d '{
"queue": "support_queue",
"agent": "agent_002",
"level": 1,
"position": 2
}'
# 3. Set agents to Available status
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_001/status \
-H "X-API-Key: your-api-key" \
-d '{"status": "Available"}'
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_002/status \
-H "X-API-Key: your-api-key" \
-d '{"status": "Available"}'
# 4. Monitor queue status
curl https://api.example.com/v1/callcenter/queues/support_queue/status \
-H "X-API-Key: your-api-key"
# 5. View members in queue (callers waiting)
curl https://api.example.com/v1/callcenter/members?queue=support_queue \
-H "X-API-Key: your-api-key"
# 6. Set agent on break
curl -X PUT https://api.example.com/v1/callcenter/agents/agent_001/status \
-H "X-API-Key: your-api-key" \
-d '{"status": "On Break"}'
# 7. View agent statistics
curl https://api.example.com/v1/callcenter/agents/agent_001 \
-H "X-API-Key: your-api-key"
Best Practices
1. Agent Contact Configuration
✅ Recommended: Use registrar gateway for NAT traversal
{
"contact": "sofia/gateway/registrar/agent-001"
}
❌ Avoid: Direct IP addresses (breaks with NAT)
{
"contact": "sofia/internal/1001@192.168.1.100"
}
2. Tier Level Design
Use tiered escalation for load balancing:
- Tier 1: Senior agents (level=1, positions 1-3)
- Tier 2: Junior agents (level=2, positions 1-5)
- Tier 3: Overflow/backup (level=3, positions 1-2)
Benefits:
- Optimizes agent utilization
- Ensures senior agents get priority
- Provides clear escalation path
3. Status Change Latency
⏱️ Remember: Database changes take ~100ms to propagate to FreeSWITCH
// ❌ BAD: Immediate status check
await updateAgentStatus('agent_001', 'Available');
const status = await getAgentStatus('agent_001'); // Might still show old status
// ✅ GOOD: Allow time for propagation
await updateAgentStatus('agent_001', 'Available');
await sleep(150); // Wait for mod_callcenter poll
const status = await getAgentStatus('agent_001');
4. Member Query Performance
For high-frequency polling, filter by queue:
# ✅ Efficient: Query specific queue
curl https://api.example.com/v1/callcenter/members?queue=support_queue
# ❌ Inefficient: Query all members across all queues
curl https://api.example.com/v1/callcenter/members
5. Wrap-Up Time
Set appropriate wrap-up times based on your workflow:
| Use Case | Recommended Wrap-Up Time |
|---|---|
| High-volume inbound | 5-10 seconds |
| Technical support | 30-60 seconds (notes) |
| Sales follow-up | 60-120 seconds (CRM) |
Related Endpoints
- Queue Management - Queue CRUD and lifecycle
- Extension Management - SIP extension management
- Telephony Call Control - Call origination and control
Source Code
Implementation files:
- Router:
routers/callcenter_direct.py - Adapter:
adapters/callcenter_db.py - Models:
models/agent.py,models/tier.py,models/member.py