Runbook Open the console →

Virtual Call-Center

An AI-run call center on the customer's own number. A whole floor - agents, team leads, and an AI management layer - with nobody to hire, schedule, or pay by the hour. This runbook is for operators.

The org model

A Virtual Call-Center staffs five roles, top of the house to the floor. Only the call-facing roles carry a voice; the management layer supervises and reports silently.

RoleLayerOn the phone?What it does
Site Manager
also Box Manager
ManagementNoOwns the whole site. Sets the operating plan and signs the daily report.
Operations ManagerManagementNoGuards the service level. Rebalances staffing across shifts to hit SLA.
Shift ManagerManagementNoRuns the shift. Opens and closes the floor and keeps the queue healthy.
Team LeadSupervisorYesRuns a pod of agents and takes the escalations agents cannot close.
Agent requiredFrontlineYesAnswers the calls, has the conversation, captures what the caller needs.

A call center is only "up" once it has at least one Agent seat. Every other role is a layer the customer can add or leave off.

How a call flows

Point a center's number VoiceUrl at POST /voice. A live center with an Agent connects the caller to the media bridge (OpenAI Realtime, G.711 mu-law both ways); an unknown number, a closed floor, or a center with no Agent seat gets a brand-neutral spoken message and hangs up.

caller → the customer's number (DID or TFN) → POST /voice
     → <Connect><Stream> to the media bridge
     → an Agent persona greets in its assigned voice, converses,
       captures name / callback / reason (capture_message)
     → needs a lead? the agent calls escalate_to_team_lead → the SAME live
       call is handed to the Team Lead persona (voice + instructions swap in
       place; no human dialed, no dead end)
     → on hangup the call is logged (agent voice, escalated?, seconds, summary)
       so the management layer rolls it into a shift report

Voices

Call-facing roles pick from a named cast (Aria, Marcus, ...) in three tiers - standard, signature, and studio (most lifelike). The raw speech engine behind each voice is never shown to a customer. The same cast powers the single AI receptionist (VocalizeCloud), so a customer who runs both hears one consistent roster.

The console

Open /console, paste your ADMIN_TOKEN, and Connect.

Build tab

Pick a call center (or + New), set the number, business name, timezone, and greeting, then staff each role with the seat steppers and give every call-facing role a voice. Save roster writes it. The monthly seat estimate updates live at the bottom.

Floor tab

The live view of the staffed org: the management row, the Team Lead row, and the wall of Agent seats, with the headcount and monthly seat cost. Open the floor takes the center live - it only works once at least one Agent seat is staffed. Close floor stops taking calls without touching the roster.

Admin API

Everything the console does is on a Bearer ADMIN_TOKEN API. Roster changes take effect immediately.

VerbPathPurpose
GET/admin/rolesRole catalog (public)
GET/admin/voicesVoice catalog, provider stripped (public)
GET/admin/centersList call centers
GET/admin/centers/:numberOne center + its resolved floor
PUT/admin/centers/:numberCreate or update a center + roster
POST/admin/centers/:number/liveOpen or close the floor ({"live":true})
GET/admin/centers/:number/reportShift report: calls, escalation rate, avg handle time (?since=)
DELETE/admin/centers/:numberRemove a center
GET/admin/requestsConcierge number requests
POST/admin/assistConsole assistant (needs OPENAI_API_KEY)
Example: stand up a center
curl -X PUT https://<host>/admin/centers/+18885330466 \
  -H "authorization: Bearer $ADMIN_TOKEN" -H "content-type: application/json" \
  -d '{"businessName":"Northside Support","timezone":"America/Chicago",
       "staffing":[
         {"roleId":"operations_manager","seats":1},
         {"roleId":"team_lead","seats":2,"voiceId":"marcus"},
         {"roleId":"agent","seats":8,"voiceId":"aria"}
       ]}'

Self-serve setup

The customer's welcome link opens /setup?grant=…, a signed deep link (same HMAC contract as the voice engine, secret CLIENT_GRANT_SECRET). They provide their number, staff the roster, pick voices, and go live without an admin token. Customers who want us to source or port a number file a concierge request there instead.

Billing

Each staffed seat carries a flat monthly USD fee. Agents and Team Leads also meter call minutes at their voice's per-minute rate. Everything is in US dollars.

Deploy & health

  1. Add a Postgres service; set DATABASE_URL. Without it the engine runs an env-only demo center so the console still works.
  2. Set ADMIN_TOKEN (openssl rand -hex 24).
  3. Optional: CLIENT_GRANT_SECRET (self-serve links), OPENAI_API_KEY (console assistant).
  4. GET /healthz touches the call_center table and reports store, role, and voice counts - a live query, not an env flag, so a dead database reads as degraded.