Skip to main content

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_callcenter polls 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:

StatusDescriptionControlled By
Logged OutAgent is not activeAgent/Supervisor
AvailableAgent is ready to receive callsAgent/Supervisor
Available (On Demand)Agent accepts calls manuallyAgent/Supervisor
On BreakAgent is on a scheduled breakAgent/Supervisor
UnknownInitial/error stateSystem
StateDescriptionControlled By
WaitingAgent is idle, waiting for a callSystem
ReceivingAgent is being offered a callSystem
In a queue callAgent is actively on a callSystem
IdleAgent is between calls (wrap-up time)System
ReservedAgent is reserved for a specific callSystem
UnknownInitial/error stateSystem

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:

FormatUse CaseExample
sofia/gateway/registrar/agent-{id}Recommended: Routes through registrar B2BUA for NAT handlingsofia/gateway/registrar/agent-001
user/{extension}@{domain}Direct SIP user lookupuser/1001@phone.example.com
sofia/internal/{extension}@{domain}Direct Sofia profile routingsofia/internal/1001@192.168.1.100
B2BUA Pattern (Recommended)

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

ParameterDescriptionDefaultRange
max_no_answerMaximum no-answer attempts before agent marked unavailable30-100
wrap_up_timeSeconds after call ends before agent is available again100-3600
reject_delay_timeDelay before offering call to agent who rejected previous call100-3600
busy_delay_timeDelay before offering call to agent who was busy600-3600
no_answer_delay_timeDelay before offering call to agent who didn't answer100-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 calls
  • Available (On Demand) - Agent accepts calls manually (not implemented in UI)
  • On Break - Agent temporarily unavailable
  • Logged 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"
Cascade Delete

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

PropertyDescriptionRange
levelPriority tier (lower = higher priority)1-100
positionPosition within tier (affects ring order)1-100
stateCurrent tier stateSee states below

Tier States

StateDescription
ReadyTier is active and can receive calls
StandbyTier is temporarily inactive
No AnswerLast call attempt resulted in no answer
OfferingCall is currently being offered via this tier
Active InboundAgent 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

StateDescription
WaitingMember is in queue, waiting for an agent
TryingCall is being offered to an agent
AnsweredAgent has answered, call is connected
AbandonedCaller 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"
Read-Only Members

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_change timestamp in the agents table
  • ready_time field tracks cumulative time in Available status
  • External systems can calculate break duration by monitoring status changes
Break Type Management

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:

FieldDescription
calls_answeredTotal calls answered
no_answer_countNumber of no-answer events
talk_timeTotal time in calls (seconds)
ready_timeTotal time in Available status (seconds)
last_bridge_startTimestamp of last call start
last_bridge_endTimestamp of last call end
last_offered_callTimestamp of last call offer
last_status_changeTimestamp of last status change

Queue Statistics

Get member counts by state:

# Via database adapter (internal use)
# Returns: { count, waiting, trying, answered, abandoned }
Performance

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

CodeStatusDescription
AGENT_ALREADY_EXISTS409Agent with this name already exists
AGENT_NOT_FOUND404Agent not found
INVALID_STATUS400Invalid agent status value
TIER_ALREADY_EXISTS409Tier assignment already exists
TIER_NOT_FOUND404Tier assignment not found
MEMBER_NOT_FOUND404Member (caller) not found
CREATE_AGENT_ERROR500Failed to create agent
UPDATE_STATUS_ERROR500Failed 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'
);
Instance ID

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_callcenter uses odbc-dsn config to poll database
  • XML-RPC used only for queue lifecycle (load/unload/reload)

Comparison: Database vs XML-RPC

OperationDatabase ApproachXML-RPC Approach
Agent CreateINSERT into agents tablecallcenter_config agent add ...
Agent StatusUPDATE agents SET status=...callcenter_config agent set status ...
Tier CreateINSERT into tiers tablecallcenter_config tier add ...
State Persistence✅ Persistent❌ Memory only
Audit Trail✅ Full history❌ No history
Query Flexibility✅ SQL queries❌ Limited
Latency~100ms poll timeImmediate
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 CaseRecommended Wrap-Up Time
High-volume inbound5-10 seconds
Technical support30-60 seconds (notes)
Sales follow-up60-120 seconds (CRM)

Source Code

Implementation files: