Skip to main content

Queue Management & mod_callcenter

This guide provides a comprehensive deep dive into queue management in the Ominis Cluster Manager, including mod_callcenter fundamentals, the one-pod-per-queue architecture, and complete lifecycle management.

What is a Queue?

At its core, a queue is a virtual waiting room for callers waiting to be connected to an available agent. Think of it like a digital version of waiting in line at a bank or store.

Key Concepts

  • Queue: A container that holds callers and manages the connection to agents
  • Callers: Also called "members" - people waiting in the queue
  • Agents: People who answer the calls
  • Strategy: Rules that determine how calls are distributed to agents
  • Tiers: Connections between agents and queues that define priority

Example Scenario: A customer calls your support line → They enter the "support" queue → Music plays while they wait → An available agent is selected based on the queue strategy → The call connects to that agent.

mod_callcenter Concepts Deep Dive

FreeSWITCH's mod_callcenter is the engine that powers queue management. Understanding its core concepts is essential for effective queue configuration.

1. Queues

A queue is the organizational container for call flow.

Properties:

  • Name: Unique identifier (e.g., "sales", "support", "billing")
  • Strategy: Algorithm for distributing calls (see Queue Strategies)
  • Configuration: Timeouts, music on hold, announcements, tier rules
  • Pod Mapping: In Ominis, each queue maps to exactly one FreeSWITCH pod

Database Schema:

CREATE TABLE cc_queues (
name VARCHAR(255) PRIMARY KEY,
strategy VARCHAR(50),
moh_sound VARCHAR(255),
announce_frequency INT,
max_wait_time INT,
tier_rules_apply BOOLEAN,
-- ... additional configuration
);

2. Agents (Call Center Agents)

Agents are the people who answer calls from queues.

Properties:

  • Name: Unique identifier for the agent
  • Type: callback (calls agent) or uuid-standby (agent calls in)
  • Contact: SIP URI where agent can be reached (e.g., user/agent_001@domain.com)
  • Status: Current availability state
  • State: Current operational state
  • Timings: Configuration for wrap-up time, delays, etc.

Agent Status

The status determines whether an agent can receive calls:

StatusDescriptionCan Receive Calls?
Logged OutAgent is not working❌ No
AvailableReady to take calls✅ Yes
Available (On Demand)Ready but requires explicit routing⚠️ Manual only
On BreakTemporarily unavailable❌ No

Agent State

The state reflects what the agent is currently doing:

StateDescription
WaitingIdle, ready for next call
ReceivingIncoming call is ringing
In a queue callCurrently on a call
IdleAvailable but not actively waiting
ReservedReserved for a specific call

Agent Contact Format

The contact field tells FreeSWITCH how to reach the agent:

# Direct SIP extension
user/1001@domain.com

# Sofia gateway
sofia/gateway/registrar/agent-10720

# SIP URI
sip:agent@192.168.1.100:5060

Example Agent:

{
"name": "john@example.com",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-10720",
"status": "Available",
"state": "Waiting",
"max_no_answer": 3,
"wrap_up_time": 10,
"busy_delay_time": 60
}

3. Tiers (Agent-Queue Associations)

Tiers connect agents to queues and define priority routing.

Why Tiers?

  • Allows agents to work multiple queues
  • Defines priority levels for overflow and escalation
  • Controls the order agents are tried within the same priority

Properties:

  • Queue: Queue name
  • Agent: Agent name
  • Level: Priority tier (1 = highest, 9 = lowest)
  • Position: Order within the same level (1 = first, 2 = second, etc.)
  • State: Current tier state (Ready, Standby, Active Inbound, etc.)

Priority System Example

Queue: "sales"

┌─────────────────────────────────────┐
│ Level 1 (Highest Priority) │
│ Position 1: john@example.com │ ← Tried first
│ Position 2: jane@example.com │ ← Tried second
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ Level 2 (Overflow) │
│ Position 1: backup@example.com │ ← Only if Level 1 unavailable
└─────────────────────────────────────┘

How It Works:

  1. Call enters "sales" queue
  2. System tries Level 1 agents first (john, then jane)
  3. If no Level 1 agent answers within configured timeout
  4. System escalates to Level 2 agents (backup)
  5. Process repeats until call is answered or times out

Tier Rules (optional):

  • tier_rules_apply: Enable tier-based waiting
  • tier_rule_wait_second: Seconds to wait before trying next level
  • tier_rule_wait_multiply_level: Multiply wait time by level (e.g., Level 2 waits 2x)
  • tier_rule_no_agent_no_wait: Don't wait if no agents available

4. Members (Callers in Queue)

Members represent inbound callers waiting in a queue.

Properties:

  • UUID: Unique identifier for the call
  • Queue: Queue name
  • Session UUID: FreeSWITCH call session ID
  • CID Number/Name: Caller ID information
  • Joined Epoch: Timestamp when caller entered queue
  • State: Current member state
  • Serving Agent: Agent currently handling the call (if answered)

Member States

StateDescription
WaitingIn queue, waiting for agent
TryingSystem is attempting to connect to agent
AnsweredConnected to an agent
AbandonedCaller hung up before being answered

Example Member:

{
"uuid": "12345678-1234-1234-1234-123456789012",
"queue": "support",
"session_uuid": "87654321-4321-4321-4321-210987654321",
"cid_number": "+1234567890",
"cid_name": "John Customer",
"joined_epoch": 1640995200,
"state": "Waiting",
"serving_agent": null
}

5. The Complete Picture

Here's how all the pieces work together:

One-Pod-Per-Queue Architecture

Ominis takes a unique approach: every queue runs in its own isolated Kubernetes pod.

Why One Pod Per Queue?

Traditional PBX systems run all queues in a single shared FreeSWITCH instance. Ominis deliberately does the opposite.

Benefits

  1. Complete Isolation

    • One queue's problems don't affect others
    • Security boundary per customer/queue
    • Independent configuration per queue
  2. Independent Scaling

    • Scale high-volume queues without affecting low-volume ones
    • Right-size resources per queue
    • Add capacity exactly where needed
  3. Fault Tolerance

    • One queue failure doesn't bring down others
    • Restart individual queues without system-wide impact
    • Easier debugging and troubleshooting
  4. Resource Allocation

    • Clear CPU/memory limits per queue
    • Predictable performance characteristics
    • Fair resource sharing via Kubernetes
  5. Multi-Tenancy

    • Separate customer data at infrastructure level
    • Compliance-friendly isolation
    • Per-tenant billing and monitoring

Trade-offs

  • More Kubernetes Resources: More pods means more cluster overhead
  • Higher Operational Complexity: More deployments to manage
  • Resource Overhead: Each pod has base FreeSWITCH overhead (~50-100MB)

Decision: The benefits of isolation, security, and scalability outweigh the resource costs for a production call center platform.

Pod Components

Each queue pod contains:

Queue Pod (e.g., freeswitch-queue-sales)
├── FreeSWITCH Process
│ ├── mod_callcenter # Queue engine
│ ├── mod_sofia # SIP stack
│ ├── mod_xml_curl # Dynamic configuration
│ ├── mod_odbc # Database integration
│ └── mod_xml_rpc # API control

├── Configuration (Kubernetes)
│ ├── Deployment # Pod definition
│ ├── ConfigMap # FreeSWITCH config
│ └── Service # Network exposure

├── Networking
│ ├── SIP Port # 5060 + offset (e.g., 5160)
│ ├── RTP Ports # 16384-32768 (subset)
│ ├── XML-RPC Port # 8080 (internal only)
│ └── Health Check Port # 8888

└── Storage (optional)
└── Recordings # Call recordings (if enabled)

Architecture Diagram

Queue Lifecycle

Creation Flow

Startup Process

  1. API Receives Request: POST /v1/queues
  2. Extension Creation: Creates SIP extension queue-{name} for authentication
  3. Password Generation: Auto-generates secure SIP password
  4. Database Storage: Saves queue configuration to PostgreSQL
  5. Template Rendering: Generates Kubernetes YAML from template
  6. K8s Apply: Creates Deployment, Service, ConfigMap
  7. Pod Start: Kubernetes schedules and starts pod
  8. FreeSWITCH Init: Loads configuration from database via ODBC
  9. mod_callcenter Load: Initializes queue with agents/tiers
  10. SIP Registration: Registers with FreeSWITCH registrar
  11. Health Check: Health server responds on port 8888
  12. Ready: Pod becomes ready, starts accepting calls

Update Process

When updating a queue (e.g., changing strategy):

  1. API Receives Update: PUT /v1/queues/sales
  2. Database Update: Updates queue configuration
  3. Template Re-render: Generates updated YAML
  4. Server-Side Apply: K8s patches existing resources (no downtime)
  5. Configuration Reload: FreeSWITCH reloads from database
  6. mod_callcenter Pickup: Polls database (~100ms), applies changes

Note: Most configuration changes are picked up without restart. For major changes (SIP ports, etc.), a restart may be required.

Restart Process

To restart a queue without deleting it:

POST /v1/queues/sales/restart

What Happens:

  1. API deletes the pod (NOT the Deployment)
  2. Kubernetes immediately recreates pod (same configuration)
  3. New pod starts, loads same config from database
  4. SIP re-registers, queue becomes ready again
  5. Downtime: ~5-15 seconds

Use Cases:

  • Apply configuration that requires restart
  • Recover from stuck state
  • Clear cache/temporary state

Deletion Process

What Gets Deleted:

  • ✅ Kubernetes Deployment
  • ✅ Kubernetes Service
  • ✅ Kubernetes ConfigMap
  • ✅ SIP extension from database
  • ⚠️ Queue config (archived, not hard-deleted)
  • ⚠️ Agent/tier assignments (preserved)
  • ⚠️ Historical call data (preserved)

Queue Strategies

The strategy determines how calls are distributed to agents.

1. ring-all

Ring all available agents simultaneously. First to answer gets the call.

Use Case: When you want fastest possible answer time and all agents are equally qualified.

Behavior:

  • Rings all Available agents in all tiers at once
  • First agent to answer wins
  • Other rings are cancelled

Example:

{
"name": "emergency",
"strategy": "ring-all",
"description": "Emergency hotline - fastest answer wins"
}

2. longest-idle-agent

Route to the agent who has been idle the longest. Default and most common strategy.

Use Case: Fair distribution of calls among agents.

Behavior:

  • Tracks last call time for each agent
  • Always tries agent with longest idle time first
  • Fair workload distribution over time

Example:

{
"name": "support",
"strategy": "longest-idle-agent",
"tier_rules_apply": true
}

3. round-robin

Distribute calls evenly in circular order.

Use Case: Ensures equal call volume per agent.

Behavior:

  • Maintains pointer to "next agent"
  • Advances pointer after each call attempt
  • Cycles through all agents repeatedly

Example:

{
"name": "sales",
"strategy": "round-robin"
}

4. top-down

Always try agents in order from position 1 to N.

Use Case: Prioritize senior agents, use juniors as overflow.

Behavior:

  • Always starts with position 1
  • Only moves to next position if previous unavailable
  • Predictable, hierarchical routing

Example:

{
"name": "vip_support",
"strategy": "top-down",
"tier_rules_apply": true
}

Agent Order:

  • Level 1, Position 1: Senior agent (gets most calls)
  • Level 1, Position 2: Mid-level agent (gets overflow)
  • Level 2, Position 1: Junior agent (gets overflow from Level 1)

5. agent-with-least-talk-time

Route to agent with lowest total talk time.

Use Case: Balance workload by actual time spent on calls.

Behavior:

  • Tracks cumulative talk time per agent
  • Routes to agent with least total talk time
  • Self-balancing over time

Example:

{
"name": "billing",
"strategy": "agent-with-least-talk-time"
}

6. agent-with-fewest-calls

Route to agent with fewest total calls answered.

Use Case: Balance by call count rather than time.

Behavior:

  • Tracks call count per agent
  • Routes to agent with lowest count
  • Useful when calls have similar durations

Example:

{
"name": "intake",
"strategy": "agent-with-fewest-calls"
}

7. sequentially-by-agent-order

Try agents in strict sequential order (like top-down, but more rigid).

Use Case: Strict priority routing with no randomness.

Behavior:

  • Strict sequential order by tier level and position
  • No load balancing considerations
  • Predictable, deterministic routing

8. random

Randomly select an available agent.

Use Case: Testing, simple distribution when fairness doesn't matter.

Behavior:

  • Random selection from available agents
  • No fairness or load balancing
  • Simple but uneven distribution

Port Allocation

Each queue pod requires dedicated ports to avoid conflicts.

Port Types

Port TypePurposeRangeExample
SIPSIP signaling5060 + offsetQueue 1: 5160, Queue 2: 5260
RTPMedia (audio) streams16384-32768 (subset)Queue 1: 16384-20000
XML-RPCAPI control8080All queues use 8080 internally
HealthHealth check8888All queues use 8888 internally

Port Offset Strategy

To avoid conflicts, queues use port offsets:

Base SIP Port: 5060

Queue "sales": SIP 5160 (5060 + 100*1)
Queue "support": SIP 5260 (5060 + 100*2)
Queue "billing": SIP 5360 (5060 + 100*3)

RTP Port Allocation

RTP ports are allocated in non-overlapping ranges:

Queue "sales":     16384-20000  (3,616 ports)
Queue "support": 20001-24000 (4,000 ports)
Queue "billing": 24001-28000 (4,000 ports)

Calculation:

  • Each concurrent call needs 2 ports (one for each side)
  • 4,000 ports = ~2,000 concurrent calls per queue
  • Adjust rtp_start_port and rtp_end_port as needed

Service Exposure

apiVersion: v1
kind: Service
metadata:
name: freeswitch-queue-sales
spec:
selector:
queue: sales
ports:
- name: sip
port: 5160
protocol: UDP
- name: xmlrpc
port: 8080
protocol: TCP
- name: health
port: 8888
protocol: TCP

ADR: One Pod Per Queue vs Shared Instance

Context

Traditional call center systems typically run all queues in a single shared FreeSWITCH instance. This is more resource-efficient but creates tight coupling between queues.

Decision

Deploy a separate Kubernetes pod for each queue.

Alternatives Considered

Alternative 1: Shared FreeSWITCH Instance

Approach: Run all queues in one FreeSWITCH instance.

Pros:

  • ✅ More resource-efficient (one process, shared memory)
  • ✅ Simpler operational model (fewer moving parts)
  • ✅ Lower Kubernetes overhead
  • ✅ Traditional approach, well-understood

Cons:

  • ❌ Single point of failure (one crash affects all queues)
  • ❌ Resource contention (high-volume queue starves others)
  • ❌ Security risks (all customers share same process)
  • ❌ Difficult to isolate and debug issues
  • ❌ Scaling affects all queues equally (can't scale just one)
  • ❌ Configuration complexity (all queues in same config)

Alternative 2: One Pod Per Queue (Chosen)

Approach: Deploy isolated pod for each queue.

Pros:

  • ✅ Complete isolation (one failure doesn't cascade)
  • ✅ Independent scaling (scale hot queues independently)
  • ✅ Clear security boundaries (customer data separation)
  • ✅ Easier debugging (isolated logs, metrics per queue)
  • ✅ Predictable performance (dedicated resources)
  • ✅ Compliance-friendly (tenant isolation)

Cons:

  • ⚠️ More Kubernetes resources (more pods to manage)
  • ⚠️ Higher memory overhead (~50-100MB base per pod)
  • ⚠️ More complex orchestration
  • ⚠️ Slightly slower to provision (K8s scheduling per pod)

Consequences

Positive

  • Security: Customer A's queue cannot access Customer B's data
  • Reliability: One queue failure doesn't affect others
  • Scalability: Scale queues independently based on load
  • Observability: Clear per-queue metrics and logs
  • Performance: No resource contention between queues

Negative

  • Cost: Higher baseline resource usage
  • Complexity: More K8s objects to manage (deployments, services, configmaps)
  • Provisioning Time: Creating queue takes ~5-15 seconds (K8s scheduling)

Mitigation

  • Use Kubernetes resource limits to right-size pods
  • Implement pod affinity/anti-affinity for optimal scheduling
  • Cache queue templates for faster provisioning
  • Use metrics to identify and consolidate underutilized queues

Status

Accepted - This is the production architecture.

API Endpoints

Queue Management

Create Queue

Creates a new queue with dedicated pod.

POST /v1/queues

Request Body:

{
"name": "sales",
"strategy": "longest-idle-agent",
"description": "Sales team queue",
"max_wait_time": 300,
"tier_rules_apply": true,
"tier_rule_wait_second": 10,
"tier_rule_no_agent_no_wait": false,
"global_codec_prefs": "OPUS,G722,PCMU,PCMA",
"outbound_codec_prefs": "OPUS,G722,PCMU,PCMA",
"max_sessions": 1000,
"sessions_per_second": 100
}

Response (201 Created):

{
"queue": {
"name": "sales",
"strategy": "longest-idle-agent",
"moh_sound": "$${hold_music}",
"max_wait_time": 300,
"tier_rules_apply": true
}
}

cURL Example:

curl -X POST http://localhost:8000/v1/queues \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"name": "sales",
"strategy": "longest-idle-agent",
"max_wait_time": 300,
"tier_rules_apply": true
}'

List Queues

Retrieve all queues.

GET /v1/queues

Response (200 OK):

{
"queues": [
{
"name": "sales",
"strategy": "longest-idle-agent",
"max_wait_time": 300
},
{
"name": "support",
"strategy": "ring-all",
"max_wait_time": 600
}
],
"total": 2
}

cURL Example:

curl -X GET http://localhost:8000/v1/queues \
-H "X-API-Key: demo"

Get Queue Details

Retrieve details for a specific queue.

GET /v1/queues/{queue_name}

Response (200 OK):

{
"queue": {
"name": "sales",
"strategy": "longest-idle-agent",
"moh_sound": "$${hold_music}",
"announce_frequency": 30,
"max_wait_time": 300,
"tier_rules_apply": true,
"tier_rule_wait_second": 10,
"max_sessions": 1000,
"global_codec_prefs": "OPUS,G722,PCMU,PCMA"
}
}

cURL Example:

curl -X GET http://localhost:8000/v1/queues/sales \
-H "X-API-Key: demo"

Update Queue

Update queue configuration.

PUT /v1/queues/{queue_name}

Request Body:

{
"strategy": "ring-all",
"max_wait_time": 600,
"announce_frequency": 45
}

Response (200 OK):

{
"queue": {
"name": "sales",
"strategy": "ring-all",
"max_wait_time": 600,
"announce_frequency": 45
}
}

cURL Example:

curl -X PUT http://localhost:8000/v1/queues/sales \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"strategy": "ring-all",
"max_wait_time": 600
}'

Restart Queue

Restart queue pod (deletes and recreates).

POST /v1/queues/{queue_name}/restart

Response (204 No Content)

cURL Example:

curl -X POST http://localhost:8000/v1/queues/sales/restart \
-H "X-API-Key: demo"

Delete Queue

Delete a queue and its pod.

DELETE /v1/queues/{queue_name}

Response (204 No Content)

cURL Example:

curl -X DELETE http://localhost:8000/v1/queues/sales \
-H "X-API-Key: demo"

Get Queue Count

Get real-time statistics for a queue.

GET /v1/queues/{queue_name}/count

Response (200 OK):

{
"queue": "sales",
"count": 5,
"waiting": 3,
"trying": 1,
"answered": 1,
"abandoned": 0
}

cURL Example:

curl -X GET http://localhost:8000/v1/queues/sales/count \
-H "X-API-Key: demo"

Complete Example: Setting Up a Queue

Let's walk through setting up a complete queue with agents and tiers.

Step 1: Create Queue

curl -X POST http://localhost:8000/v1/queues \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"name": "support",
"strategy": "longest-idle-agent",
"max_wait_time": 300,
"tier_rules_apply": true,
"tier_rule_wait_second": 10
}'

Step 2: Create Agents

# Senior Agent (John)
curl -X POST http://localhost:8000/v1/agents \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"name": "john@example.com",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-10720",
"max_no_answer": 3,
"wrap_up_time": 10
}'

# Mid-level Agent (Jane)
curl -X POST http://localhost:8000/v1/agents \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"name": "jane@example.com",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-10721",
"max_no_answer": 3,
"wrap_up_time": 10
}'

# Junior Agent (Bob)
curl -X POST http://localhost:8000/v1/agents \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"name": "bob@example.com",
"type": "callback",
"contact": "sofia/gateway/registrar/agent-10722",
"max_no_answer": 3,
"wrap_up_time": 10
}'

Step 3: Assign Agents to Queue (Tiers)

# John: Level 1, Position 1 (first priority)
curl -X POST http://localhost:8000/v1/tiers \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"queue": "support",
"agent": "john@example.com",
"level": 1,
"position": 1,
"state": "Ready"
}'

# Jane: Level 1, Position 2 (second priority)
curl -X POST http://localhost:8000/v1/tiers \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"queue": "support",
"agent": "jane@example.com",
"level": 1,
"position": 2,
"state": "Ready"
}'

# Bob: Level 2, Position 1 (overflow only)
curl -X POST http://localhost:8000/v1/tiers \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{
"queue": "support",
"agent": "bob@example.com",
"level": 2,
"position": 1,
"state": "Ready"
}'

Step 4: Set Agents to Available

curl -X PUT http://localhost:8000/v1/agents/john@example.com/status \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{"status": "Available"}'

curl -X PUT http://localhost:8000/v1/agents/jane@example.com/status \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{"status": "Available"}'

curl -X PUT http://localhost:8000/v1/agents/bob@example.com/status \
-H "X-API-Key: demo" \
-H "Content-Type: application/json" \
-d '{"status": "Available"}'

Step 5: Test Call Flow

When a call comes in:

  1. Enters "support" queue
  2. Plays hold music
  3. System tries Level 1 agents:
    • Rings John (Level 1, Position 1)
    • If John doesn't answer in 10s, rings Jane (Level 1, Position 2)
  4. If no Level 1 agent available:
    • After tier_rule_wait_second (10s), escalates to Level 2
    • Rings Bob (Level 2, Position 1)
  5. Agent answers: Call connects
  6. Agent status: Changes to "In a queue call"
  7. After call ends: Agent enters wrap-up time (10s)
  8. Agent ready again: Status returns to "Available"

Call Distribution Flow

Monitoring and Metrics

Queue Metrics

Monitor queue health via:

# Real-time queue statistics
GET /v1/queues/{queue_name}/count

# Queue status (via XML-RPC)
GET /v1/queues/{queue_name}/status

# List all members (callers) in queue
GET /v1/members?queue=support

# List agents for queue
GET /v1/agents?queue=support

Key Metrics to Track

  • Waiting: Callers currently in queue
  • Trying: Calls attempting to connect to agents
  • Answered: Active calls in progress
  • Abandoned: Callers who hung up before answer
  • Average Wait Time: Time from join to answer
  • Agent Utilization: Percentage of time agents are on calls

Troubleshooting

Queue Pod Won't Start

Symptoms: Pod stuck in Pending or CrashLoopBackOff

Check:

kubectl get pods -n client-demo-client | grep queue-{name}
kubectl logs -n client-demo-client freeswitch-queue-{name}
kubectl describe pod -n client-demo-client freeswitch-queue-{name}

Common Causes:

  • Database connection failure (check ODBC config)
  • Port conflict (ensure unique SIP/RTP ports)
  • Resource limits too low (increase CPU/memory)
  • ConfigMap not created (check kubectl get configmap)

Agents Not Receiving Calls

Symptoms: Calls wait in queue, agents show "Available" but don't ring

Check:

  1. Agent Status: Must be "Available" (not "Logged Out" or "On Break")

    GET /v1/agents/{agent_name}
  2. Tier Assignment: Agent must be assigned to queue

    GET /v1/tiers?queue={queue_name}&agent={agent_name}
  3. Tier State: Must be "Ready" (not "Standby")

    PUT /v1/tiers/{queue}/{agent} -d '{"state": "Ready"}'
  4. Agent Contact: Verify SIP URI is correct

    # Test if agent can be reached
    # From FreeSWITCH CLI: originate sofia/gateway/registrar/agent-10720 &echo

Calls Not Entering Queue

Symptoms: Calls fail before entering queue

Check:

  1. Queue Pod Running: Verify pod is healthy

    kubectl get pods -n client-demo-client | grep queue-{name}
  2. SIP Registration: Queue should register with registrar

    # From registrar CLI: sofia status profile internal reg
  3. Dialplan: Verify dialplan routes to queue

    # Check dialplan routes caller to: callcenter {queue_name}
  4. Database Config: Verify queue exists in database

    SELECT * FROM cc_queues WHERE name = 'queue_name';

High Wait Times

Symptoms: Callers wait too long before being answered

Solutions:

  1. Add More Agents: Increase agent count
  2. Adjust Tier Levels: Reduce tier_rule_wait_second for faster escalation
  3. Change Strategy: Try "ring-all" for faster answer
  4. Check Agent Status: Ensure agents are "Available" not "On Break"
  5. Monitor Agent Load: Check if agents are busy on other calls

Summary

Queue management is the core feature of Ominis Cluster Manager. Key takeaways:

Queues are virtual waiting rooms for callers ✅ Agents are people who answer calls ✅ Tiers connect agents to queues with priority routing ✅ Members are callers waiting in queue ✅ Strategies determine how calls are distributed ✅ One pod per queue provides isolation, scaling, and fault tolerance ✅ Complete API for queue lifecycle management ✅ Real-time metrics for monitoring and optimization

With this foundation, you can build sophisticated call center workflows with predictable, scalable infrastructure.