Call Center Setup Guide
This guide walks you through setting up a fully functional call center using the Semaswift Voice API.
Overview
A typical call center setup involves:
- Creating agents (call center operators)
- Setting up queues (routing rules)
- Configuring DIDs (phone numbers), optionally with sticky routing for returning callers
- Defining business hours
- Setting up IVR menus
Prerequisites
- Semaswift account with Voice module enabled
- Admin or Supervisor role
- At least one DID (phone number) provisioned
Step 1: Create Agents
Agents are users who handle calls. Create agents with their queue assignments:
POST /api/v2/voice/agents
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"user_id": "usr_123456",
"extension": "1001",
"queues": ["queue_support", "queue_sales"],
"max_concurrent_calls": 2,
"wrap_up_time": 30
}
Step 2: Configure Queues
Queues define how calls are routed to agents:
POST /api/v2/voice/queues
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"name": "Support Queue",
"strategy": "round_robin",
"timeout": 30,
"max_wait_time": 300,
"music_on_hold": "default",
"announce_position": true,
"announce_hold_time": true
}
Queue Strategies
| Strategy | Description |
|---|---|
round_robin | Distribute evenly among available agents |
least_recent | Route to agent who hasn't taken a call longest |
fewest_calls | Route to agent with fewest calls today |
random | Random available agent |
ring_all | Ring all available agents |
Step 3: Assign DIDs
Connect phone numbers to queues:
POST /api/v2/voice/dids
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"number": "+1-555-123-4567",
"destination_type": "queue",
"destination_id": "queue_support",
"fallback_destination": "voicemail"
}
Route returning callers to their agent (optional)
Outbound teams often reach a customer's voicemail, and the customer calls straight back. With sticky routing, a customer who missed an agent's call is connected to that agent instead of the first free agent in the queue.
Enable it per DID:
PATCH /api/v1/voice/dids/{id}
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"stickyRouting": {
"enabled": true,
"lookbackHours": 24,
"ringSeconds": 15,
"fallbackQueueId": 12,
"announcementSoundId": 41,
"fallbackSoundId": 42
}
}
| Field | Default | Description |
|---|---|---|
enabled | false | Turns sticky routing on for this DID |
lookbackHours | 24 | How far back to look for a missed outbound call (1–168) |
ringSeconds | 15 | How long the agent's phone rings before falling back (5–60) |
fallbackQueueId | — | Queue for callers whose agent is unavailable or does not answer. Required when enabled |
announcementSoundId | none | Optional sound played before ringing the agent, e.g. "Connecting you to the agent who called you" |
fallbackSoundId | none | Optional sound played before transferring to the fallback queue |
The queue and sounds must belong to your organization. Upload sounds through the Voice sounds API first.
How a call is routed. Business hours are checked first, so a closed line still plays its out-of-hours message. Otherwise Semaswift finds the most recent outbound call to the caller's number within lookbackHours. Only that call counts, and only if the customer did not pick it up (no answer, busy or cancelled):
| Most recent outbound call | What happens |
|---|---|
| Placed by an agent who is available | The agent's phone rings for ringSeconds. If they don't answer, the caller goes to fallbackQueueId |
| Placed by an agent who is offline, paused, on another call or in wrap-up | Straight to fallbackQueueId, without ringing |
| Placed by a campaign with no agent attached (progressive, predictive, power, robocall) | The campaign's queue, or fallbackQueueId if the campaign has none |
| Answered by the customer, or no outbound call in the window | The DID's normal destination (IVR, queue or agent) |
Sticky routing never blocks a call. If it can't decide, for example because the caller ID is withheld, the call follows the DID's normal destination.
Checking the outcome. Each routed call's call log records the result under metadata.sticky_routing:
{
"sticky_routing": {
"matched_call_log_id": 98123,
"outcome": "connected"
}
}
outcome is one of connected (the agent answered), no_answer (the agent was rung but didn't answer, so the call went to the queue), agent_unavailable or campaign_queue.
Preview campaign calls are dialled by the agent, so they count as that agent's calls.
To turn sticky routing off, send "stickyRouting": {"enabled": false}.
Step 4: Configure Business Hours
Set when your call center is open:
POST /api/v2/voice/business-hours
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"name": "Standard Hours",
"timezone": "America/New_York",
"schedule": {
"monday": {"open": "09:00", "close": "17:00"},
"tuesday": {"open": "09:00", "close": "17:00"},
"wednesday": {"open": "09:00", "close": "17:00"},
"thursday": {"open": "09:00", "close": "17:00"},
"friday": {"open": "09:00", "close": "17:00"},
"saturday": null,
"sunday": null
},
"closed_message": "We are currently closed. Our hours are Monday-Friday 9AM-5PM EST."
}
Step 5: Set Up IVR (Optional)
Create an interactive voice menu:
POST /api/v2/voice/ivr
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"name": "Main Menu",
"greeting": "Welcome to Acme Support. Press 1 for Sales, 2 for Support, or 0 for an operator.",
"options": [
{"digit": "1", "destination_type": "queue", "destination_id": "queue_sales"},
{"digit": "2", "destination_type": "queue", "destination_id": "queue_support"},
{"digit": "0", "destination_type": "extension", "destination_id": "1001"}
],
"timeout_destination": "queue_support",
"invalid_destination": "replay"
}
Testing Your Setup
- Check agent status: Agents should show as "available"
- Test inbound call: Call your DID and verify routing
- Monitor queue: Watch real-time queue statistics
- Review CDRs: Check call detail records for issues
Real-Time Monitoring
Connect to WebSocket for live updates:
const ws = new WebSocket('wss://realtime.semaswift.africa/connection/websocket');
ws.onopen = () => {
ws.send(JSON.stringify({
subscribe: {
channel: 'voice:queue_support'
}
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Queue update:', data);
};
Next Steps
- Agent Management - Detailed agent operations
- Configure call recordings in the Voice API settings
- Generate call reports through the Voice API