Getting Started

CXCo is an API-first platform that enables companies to create, deploy, and manage AI-powered voice and WhatsApp agents. Provision a phone number, configure an agent's personality and voice, and go live -- all through a single integration.

Base URL

https://cxco.xgmi.com/api

Authentication

All API requests require an API key passed in the X-API-Key header. There are two types of API keys:

Company Keys

Prefix: cxco_comp_k_

Issued when a company is created. Used for managing users, webhooks, providers, and company settings.

User Keys

Prefix: cxco_user_k_

Generated when a user is created. Used for managing agents, conversations, phone numbers, voices, and usage.

X-API-Key: cxco_user_k_abc123...

Error Handling

All errors follow a consistent JSON structure:

{
  "detail": "Human-readable error message",
  "error_type": "validation_error"
}
CodeMeaning
200Success
201Created
204No Content
400Bad request / validation error
401Unauthorized -- missing or invalid API key
403Forbidden -- insufficient permissions (wrong key type)
404Resource not found
409Conflict -- duplicate or constraint violation
422Unprocessable entity
500Internal server error

Pagination

List endpoints use cursor-based pagination. Pass the cursor query parameter from the previous response to fetch the next page. The default page size is 30 (max 100).

Guides

Step-by-step walkthroughs for common API workflows.

Create and Publish a Voice Agent

Go from zero to a live AI agent answering phone calls in 3 API calls. Browse available voices, create a draft agent, and publish it to get a phone number.

1
GET /api/voices/

List voices

Browse the curated catalogue of available AI voices.

Example Response
{
  "id": 1,
  "provider_id": 1,
  "external_voice_id": "21m00Tcm4TlvDq8ikWAM",
  "name": "Rachel",
  "gender": "female",
  "accent": "american",
  "language": "en",
  "preview_url": "https://cdn.example.com/voices/rachel_preview.mp3",
  "tags": {
    "style": "conversational",
    "tone": "warm"
  },
  "status": "active"
}
View full endpoint reference →
2
POST /api/agents/

Create a draft agent

Create a new AI agent in draft status.

Request Body
{
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp. You help customers understand our product range and guide them toward the right solution. Be conversational, never pushy.",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice_id": 1,
  "language": "en",
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  }
}
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "draft",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
View full endpoint reference →
3
POST /api/agents/{agent_id}/publish

Publish agent

Publish a draft agent to make it live.

Request Body
{
  "country_code": "ZA"
}
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "active",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
View full endpoint reference →

Enable WhatsAppComing Soon

Add WhatsApp messaging to an existing agent's phone number. The same number handles both voice calls and WhatsApp messages through the same agent personality.

1
POST /api/phone-numbers/{phone_number_id}/enable-whatsapp

Enable WhatsApp

Initiate WhatsApp Business verification for a phone number.

Example Response
{
  "id": 5,
  "number": "+27211234567",
  "provider_slug": "sip_trunk",
  "country_code": "ZA",
  "status": "assigned",
  "company_id": 1,
  "agent_id": 10,
  "whatsapp_status": "pending",
  "created_at": "2026-03-01T00:00:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
View full endpoint reference →
2
GET /api/phone-numbers/{phone_number_id}/whatsapp-status

Check WhatsApp status

Check the WhatsApp verification status of a phone number.

Example Response
{
  "id": 5,
  "number": "+27211234567",
  "whatsapp_status": "active"
}
View full endpoint reference →

Set Up Webhooks

Receive real-time events when conversations complete, agents change status, or WhatsApp verification finishes. Webhooks use HMAC signatures for verification.

1
POST /api/webhooks/

Register webhook

Register an HTTPS endpoint to receive real-time events.

Request Body
{
  "url": "https://your-app.com/webhooks/cxco",
  "events": [
    "conversation.completed",
    "agent.status_changed"
  ],
  "secret": "whsec_your_secret_key"
}
Example Response
{
  "id": 1,
  "company_id": 1,
  "url": "https://your-app.com/webhooks/cxco",
  "events": [
    "conversation.completed",
    "agent.status_changed"
  ],
  "status": "active",
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-12T10:00:00Z"
}
View full endpoint reference →
2
GET /api/webhooks/

List webhooks

List all webhook registrations for your company.

Example Response
{
  "id": 1,
  "company_id": 1,
  "url": "https://your-app.com/webhooks/cxco",
  "events": [
    "conversation.completed",
    "agent.status_changed"
  ],
  "status": "active",
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-12T10:00:00Z"
}
View full endpoint reference →

Monitor Usage and Conversations

Track conversation history with full transcripts, and monitor voice minutes and message counts across your agents.

1
GET /api/conversations/

List conversations

List conversations with cursor-based pagination.

Example Response
{
  "id": 100,
  "agent_id": 10,
  "company_id": 1,
  "channel": "voice",
  "caller_number": "+27821234567",
  "direction": "inbound",
  "started_at": "2026-03-12T16:05:00Z",
  "ended_at": "2026-03-12T16:08:32Z",
  "duration_seconds": 212,
  "message_count": 14,
  "status": "completed",
  "call_successful": true,
  "transcript_summary": "Customer enquired about Pro plan pricing. Agent provided details and offered to send a follow-up email.",
  "source": "elevenlabs",
  "external_conversation_id": "el_conv_abc123",
  "created_at": "2026-03-12T16:05:00Z"
}
View full endpoint reference →
2
GET /api/conversations/{conversation_id}

Get conversation details

Retrieve the full details of a conversation, including the complete transcript and metadata.

Example Response
{
  "id": 100,
  "agent_id": 10,
  "company_id": 1,
  "channel": "voice",
  "caller_number": "+27821234567",
  "direction": "inbound",
  "started_at": "2026-03-12T16:05:00Z",
  "ended_at": "2026-03-12T16:08:32Z",
  "duration_seconds": 212,
  "message_count": 14,
  "status": "completed",
  "call_successful": true,
  "transcript_summary": "Customer enquired about Pro plan pricing. Agent provided details and offered to send a follow-up email.",
  "source": "elevenlabs",
  "external_conversation_id": "el_conv_abc123",
  "created_at": "2026-03-12T16:05:00Z",
  "transcript": [
    {
      "role": "agent",
      "message": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
      "timestamp": 0.0
    },
    {
      "role": "user",
      "message": "Hi, I was wondering about your pricing for the Pro plan.",
      "timestamp": 3.2
    },
    {
      "role": "agent",
      "message": "Great question! Our Pro plan starts at R499 per month and includes...",
      "timestamp": 5.8
    }
  ],
  "metadata_": {
    "llm_tokens": 1240
  }
}
View full endpoint reference →
3
GET /api/usage/summary

Get usage summary

Get aggregated usage metrics for your company over a date range.

Example Response
{
  "period": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-12"
  },
  "voice": {
    "total_conversations": 342,
    "total_minutes": 1205.7,
    "total_sip_refer_minutes": 87.3
  },
  "whatsapp": {
    "total_conversations": 156,
    "total_messages": 2840
  }
}
View full endpoint reference →
4
GET /api/usage/daily

Get daily usage breakdown

Get usage metrics broken down by day.

Example Response
{
  "date": "2026-03-11",
  "voice": {
    "total_conversations": 31,
    "total_minutes": 112.4,
    "total_sip_refer_minutes": 8.2
  },
  "whatsapp": {
    "total_conversations": 18,
    "total_messages": 245
  }
}
View full endpoint reference →

API Reference

Complete reference for all CXCo API endpoints.

Companies

A company is the top-level tenant. All agents, phone numbers, users, and usage roll up to a company. Each company has its own credentials and billing scope.

GET /api/companies/{company_id} Any Key

Get company details

Retrieve the details of a company by ID. You can only access the company associated with your API key.
NameInTypeRequiredDescription
company_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
namestringrequired
statusstringrequired
meta_business_idstring?optional
settingsobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 1,
  "name": "Acme Corp",
  "status": "active",
  "settings": {
    "default_language": "en"
  },
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-12T10:00:00Z"
}
StatusDescription
404The company ID does not match your API key's company.
422Validation Error
PUT /api/companies/{company_id} Any Key

Update company

Update a company's name or settings. Only provided fields are updated.
NameInTypeRequiredDescription
company_idpathintegerrequired
FieldTypeRequiredDescription
namestring?optional
settingsobject?optional
Example
{
  "name": "Acme Corp International",
  "settings": {
    "default_language": "en",
    "timezone": "Africa/Johannesburg"
  }
}
FieldTypeRequiredDescription
idintegerrequired
namestringrequired
statusstringrequired
meta_business_idstring?optional
settingsobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 1,
  "name": "Acme Corp International",
  "status": "active",
  "settings": {
    "default_language": "en",
    "timezone": "Africa/Johannesburg"
  },
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-12T14:30:00Z"
}
StatusDescription
422Validation Error
GET /api/companies/{company_id}/usage Any Key

Get company usage

Get aggregated usage metrics for your company. Optionally filter by date range. Returns voice minutes, conversation counts, and per-agent breakdowns.
NameInTypeRequiredDescription
company_idpathintegerrequired
start_datequerystringoptional
end_datequerystringoptional
FieldTypeRequiredDescription
periodobjectrequired
voiceobjectrequired
agents_activeintegeroptional Default: 0
by_agentarray[AgentUsageBreakdown]optional Default: []
Example Response
{
  "period": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-12"
  },
  "voice": {
    "total_conversations": 342,
    "total_minutes": 1205.7,
    "total_sip_refer_minutes": 87.3,
    "average_duration_seconds": 211.5,
    "success_rate": 0.94
  },
  "agents_active": 5,
  "by_agent": [
    {
      "agent_id": 10,
      "agent_name": "Support Bot",
      "voice_minutes": 620.3,
      "total_conversations": 180
    }
  ]
}
StatusDescription
422Validation Error

Users

Users belong to a company and interact with the platform on that company's behalf. Users are created with company credentials and receive their own API keys.

POST /api/users/ Company Key

Create a user

Create a new user within your company and issue an API key. The API key is returned only once in the response -- store it securely. Users authenticate with their own key for day-to-day operations.
The api_key field is only present in this response. It cannot be retrieved later. If lost, create a new user.
FieldTypeRequiredDescription
emailstringrequired
namestringrequired
rolestringoptional Default: member
Example
{
  "email": "jane@acme.com",
  "name": "Jane Smith",
  "role": "admin"
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
emailstringrequired
namestringrequired
rolestringrequired
statusstringrequired
created_atstringrequired
updated_atstringrequired
api_keystringrequired
Example Response
{
  "id": 42,
  "company_id": 1,
  "email": "jane@acme.com",
  "name": "Jane Smith",
  "role": "admin",
  "status": "active",
  "created_at": "2026-03-12T10:05:00Z",
  "updated_at": "2026-03-12T10:05:00Z",
  "api_key": "cxco_user_k_a1b2c3d4e5f6..."
}
StatusDescription
409A user with this email already exists in the company.
422Validation Error
GET /api/users/me User Key

Get current user

Retrieve the user profile associated with the API key used in the request.
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
emailstringrequired
namestringrequired
rolestringrequired
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 42,
  "company_id": 1,
  "email": "jane@acme.com",
  "name": "Jane Smith",
  "role": "admin",
  "status": "active",
  "created_at": "2026-03-12T10:05:00Z",
  "updated_at": "2026-03-12T10:05:00Z"
}
GET /api/users/{user_id} Company Key

Get user by ID

Retrieve a specific user's profile by their ID. Requires company credentials.
NameInTypeRequiredDescription
user_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
emailstringrequired
namestringrequired
rolestringrequired
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 42,
  "company_id": 1,
  "email": "jane@acme.com",
  "name": "Jane Smith",
  "role": "admin",
  "status": "active",
  "created_at": "2026-03-12T10:05:00Z",
  "updated_at": "2026-03-12T10:05:00Z"
}
StatusDescription
404The user does not exist or belongs to a different company.
422Validation Error
PUT /api/users/{user_id} Company Key

Update user

Update a user's name or role. Only provided fields are updated. Requires company credentials.
NameInTypeRequiredDescription
user_idpathintegerrequired
FieldTypeRequiredDescription
namestring?optional
rolestring?optional
Example
{
  "name": "Jane Smith-Jones",
  "role": "owner"
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
emailstringrequired
namestringrequired
rolestringrequired
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 42,
  "company_id": 1,
  "email": "jane@acme.com",
  "name": "Jane Smith-Jones",
  "role": "owner",
  "status": "active",
  "created_at": "2026-03-12T10:05:00Z",
  "updated_at": "2026-03-12T15:00:00Z"
}
StatusDescription
404The user does not exist or belongs to a different company.
422Validation Error
DELETE /api/users/{user_id} Company Key

Delete user

Permanently delete a user and revoke their API key. This action cannot be undone. Requires company credentials.
NameInTypeRequiredDescription
user_idpathintegerrequired
Example Response
"204 No Content"
StatusDescription
404The user does not exist or belongs to a different company.
422Validation Error

Agents

An agent is a configured AI persona with a name, personality, opening line, and voice. Once published, the agent is reachable on its assigned phone number via voice call, WhatsApp, or both.

POST /api/agents/ User Key

Create a draft agent

Create a new AI agent in draft status. Configure the agent's personality (system prompt), opening line, voice, and optional advanced settings. The agent must be published before it can receive calls or messages.
The agent is created in draft status. Call POST /api/agents/{id}/publish to go live.
FieldTypeRequiredDescription
namestringrequired
personalitystringrequired
opening_linestringrequired
voice_idintegerrequired
languagestringoptional Default: en
configobject?optional
Example
{
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp. You help customers understand our product range and guide them toward the right solution. Be conversational, never pushy.",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice_id": 1,
  "language": "en",
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  }
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "draft",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
422Validation Error
GET /api/agents/ User Key

List agents

List all agents belonging to your company. Optionally filter by status (draft, active, paused, archived).
NameInTypeRequiredDescription
statusquerystringoptional
limitqueryintegeroptional Default: 30
cursorquerystringoptional
FieldTypeRequiredDescription
itemsarray[object]required
has_morebooleanrequired
next_cursorstring?optional
Example Response
[
  {
    "id": 10,
    "company_id": 1,
    "name": "Sales Assistant",
    "personality": "You are a friendly and professional sales assistant for Acme Corp...",
    "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
    "voice": {
      "id": 1,
      "name": "Rachel"
    },
    "language": "en",
    "status": "active",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "external_agent_id": "el_abc123",
    "phone_number": {
      "id": 5,
      "number": "+27211234567",
      "voice_enabled": true,
      "whatsapp_enabled": false,
      "whatsapp_status": "none"
    },
    "config": {
      "temperature": 0.7,
      "max_call_duration_seconds": 600
    },
    "created_at": "2026-03-12T14:30:00Z",
    "updated_at": "2026-03-12T14:35:00Z"
  }
]
StatusDescription
422Validation Error
GET /api/agents/{agent_id} User Key

Get agent details

Retrieve the full details of an agent, including nested voice and phone number information.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "active",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
PUT /api/agents/{agent_id} User Key

Update agent

Update an agent's configuration. Only provided fields are updated. Changes take effect on the next conversation.
Agents can be updated at any time, including while active. Changes take effect on the next conversation.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
namestring?optional
personalitystring?optional
opening_linestring?optional
voice_idinteger?optional
languagestring?optional
configobject?optional
Example
{
  "personality": "You are a friendly and professional sales assistant for Acme Corp. Always mention our current promotion: 20% off all Pro plans this month.",
  "opening_line": "Hi! Welcome to Acme Corp -- did you know we have 20% off Pro plans right now? How can I help?"
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "active",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
DELETE /api/agents/{agent_id} User Key

Archive agent

Permanently decommission an agent. The phone number is released back to the pool and the agent can no longer receive calls or messages. This action cannot be undone.
Archiving releases the phone number. If you want to temporarily stop the agent, use pause instead.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "archived",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
POST /api/agents/{agent_id}/publish User Key

Publish agent

Publish a draft agent to make it live. This provisions a phone number from the pool, creates the agent on the underlying AI platform, and connects the number to the agent. The agent's status changes from draft to active.
The agent is now live. Calls to the assigned phone number will be answered by the AI agent.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
country_codestringoptional Default: ZA
Example
{
  "country_code": "ZA"
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "active",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
POST /api/agents/{agent_id}/pause User Key

Pause agent

Temporarily take an agent offline. The phone number is retained but inbound calls and messages are not answered. Use resume to reactivate.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "paused",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
POST /api/agents/{agent_id}/resume User Key

Resume agent

Reactivate a paused agent. The agent's status returns to active and it begins receiving calls and messages again.
NameInTypeRequiredDescription
agent_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
namestringrequired
personalitystringrequired
opening_linestringrequired
voiceobjectrequired
languagestringrequired
statusstringrequired
voice_enabledbooleanrequired
whatsapp_enabledbooleanrequired
external_agent_idstring?optional
phone_numberobject?optional
configobject?optional
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 10,
  "company_id": 1,
  "name": "Sales Assistant",
  "personality": "You are a friendly and professional sales assistant for Acme Corp...",
  "opening_line": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
  "voice": {
    "id": 1,
    "name": "Rachel"
  },
  "language": "en",
  "status": "active",
  "voice_enabled": true,
  "whatsapp_enabled": false,
  "external_agent_id": "el_abc123",
  "phone_number": {
    "id": 5,
    "number": "+27211234567",
    "voice_enabled": true,
    "whatsapp_enabled": false,
    "whatsapp_status": "none"
  },
  "config": {
    "temperature": 0.7,
    "max_call_duration_seconds": 600
  },
  "created_at": "2026-03-12T14:30:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
GET /api/agents/{agent_id}/conversations User Key

List agent conversations

List conversations for a specific agent with cursor-based pagination. Optionally filter by channel (voice/whatsapp).
NameInTypeRequiredDescription
agent_idpathintegerrequired
channelquerystringoptional
limitqueryintegeroptional Default: 30
cursorquerystringoptional
FieldTypeRequiredDescription
itemsarray[object]required
has_morebooleanrequired
next_cursorstring?optional
Example Response
{
  "items": [
    {
      "id": 100,
      "agent_id": 10,
      "company_id": 1,
      "channel": "voice",
      "caller_number": "+27821234567",
      "direction": "inbound",
      "started_at": "2026-03-12T16:05:00Z",
      "ended_at": "2026-03-12T16:08:32Z",
      "duration_seconds": 212,
      "message_count": 14,
      "status": "completed",
      "call_successful": true
    }
  ],
  "has_more": true,
  "next_cursor": "eyJpZCI6IDk5fQ=="
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error
GET /api/agents/{agent_id}/usage User Key

Get agent usage

Get usage metrics for a specific agent. Optionally filter by date range. Returns voice minutes and conversation counts.
NameInTypeRequiredDescription
agent_idpathintegerrequired
start_datequerystringoptional
end_datequerystringoptional
Example Response
{
  "agent_id": 10,
  "period": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-12"
  },
  "voice": {
    "total_conversations": 180,
    "total_minutes": 620.3,
    "total_sip_refer_minutes": 42.1,
    "average_duration_seconds": 206.8,
    "success_rate": 0.95
  }
}
StatusDescription
404The agent does not exist or belongs to a different company.
422Validation Error

Voices

A curated catalogue of AI voices with preview audio samples. Each voice has attributes like gender, accent, and language. Choose a voice when creating an agent.

GET /api/voices/ Any Key

List voices

Browse the curated catalogue of available AI voices. Filter by language, gender, or accent to find the right voice for your agent.
NameInTypeRequiredDescription
languagequerystringoptional
genderquerystringoptional
accentquerystringoptional
FieldTypeRequiredDescription
idintegerrequired
provider_idintegerrequired
external_voice_idstringrequired
namestringrequired
genderstring?optional
accentstring?optional
languagestringrequired
preview_urlstring?optional
tagsobject?optional
statusstringrequired
Example Response
[
  {
    "id": 1,
    "provider_id": 1,
    "external_voice_id": "21m00Tcm4TlvDq8ikWAM",
    "name": "Rachel",
    "gender": "female",
    "accent": "american",
    "language": "en",
    "preview_url": "https://cdn.example.com/voices/rachel_preview.mp3",
    "tags": {
      "style": "conversational",
      "tone": "warm"
    },
    "status": "active"
  },
  {
    "id": 2,
    "provider_id": 1,
    "external_voice_id": "29vD33N1CtxCmqQRPOHJ",
    "name": "James",
    "gender": "male",
    "accent": "british",
    "language": "en",
    "preview_url": "https://cdn.example.com/voices/james_preview.mp3",
    "tags": {
      "style": "authoritative",
      "tone": "calm"
    },
    "status": "active"
  }
]
StatusDescription
422Validation Error
GET /api/voices/{voice_id} Any Key

Get voice details

Retrieve the full details of a specific voice, including preview audio URL and tags.
NameInTypeRequiredDescription
voice_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
provider_idintegerrequired
external_voice_idstringrequired
namestringrequired
genderstring?optional
accentstring?optional
languagestringrequired
preview_urlstring?optional
tagsobject?optional
statusstringrequired
Example Response
{
  "id": 1,
  "provider_id": 1,
  "external_voice_id": "21m00Tcm4TlvDq8ikWAM",
  "name": "Rachel",
  "gender": "female",
  "accent": "american",
  "language": "en",
  "preview_url": "https://cdn.example.com/voices/rachel_preview.mp3",
  "tags": {
    "style": "conversational",
    "tone": "warm"
  },
  "status": "active"
}
StatusDescription
404No voice exists with the given ID.
422Validation Error

Phone Numbers

Phone numbers are provisioned from a pool and assigned to agents during publishing. A single number can serve both voice and WhatsApp channels simultaneously.

GET /api/phone-numbers/available User Key

List available phone numbers

List phone numbers available for provisioning. Optionally filter by country code. Numbers are assigned to agents during the publish step.
NameInTypeRequiredDescription
country_codequerystringoptional
FieldTypeRequiredDescription
idintegerrequired
numberstringrequired
provider_slugstringrequired
country_codestringrequired
statusstringrequired
company_idinteger?required
agent_idinteger?required
whatsapp_statusstringrequired
whatsapp_external_idstring?required
created_atstringrequired
updated_atstringrequired
Example Response
[
  {
    "id": 20,
    "number": "+27217654321",
    "provider_slug": "sip_trunk",
    "country_code": "ZA",
    "status": "available",
    "whatsapp_status": "none",
    "created_at": "2026-03-01T00:00:00Z",
    "updated_at": "2026-03-12T14:35:00Z"
  }
]
StatusDescription
422Validation Error
GET /api/phone-numbers/ User Key

List company phone numbers

List all phone numbers currently assigned to your company, including their agent assignments and WhatsApp status.
FieldTypeRequiredDescription
idintegerrequired
numberstringrequired
provider_slugstringrequired
country_codestringrequired
statusstringrequired
company_idinteger?required
agent_idinteger?required
whatsapp_statusstringrequired
whatsapp_external_idstring?required
created_atstringrequired
updated_atstringrequired
Example Response
[
  {
    "id": 5,
    "number": "+27211234567",
    "provider_slug": "sip_trunk",
    "country_code": "ZA",
    "status": "assigned",
    "company_id": 1,
    "agent_id": 10,
    "whatsapp_status": "none",
    "created_at": "2026-03-01T00:00:00Z",
    "updated_at": "2026-03-12T14:35:00Z"
  }
]
POST /api/phone-numbers/{phone_number_id}/enable-whatsapp User Key

Enable WhatsApp

Initiate WhatsApp Business verification for a phone number. The number must be assigned to an agent. The verification process runs asynchronously -- poll the status endpoint to check progress.
WhatsApp enablement involves an asynchronous verification process. Use the status endpoint to check progress.
NameInTypeRequiredDescription
phone_number_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
numberstringrequired
provider_slugstringrequired
country_codestringrequired
statusstringrequired
company_idinteger?required
agent_idinteger?required
whatsapp_statusstringrequired
whatsapp_external_idstring?required
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 5,
  "number": "+27211234567",
  "provider_slug": "sip_trunk",
  "country_code": "ZA",
  "status": "assigned",
  "company_id": 1,
  "agent_id": 10,
  "whatsapp_status": "pending",
  "created_at": "2026-03-01T00:00:00Z",
  "updated_at": "2026-03-12T14:35:00Z"
}
StatusDescription
404The phone number does not exist or belongs to a different company.
422Validation Error
GET /api/phone-numbers/{phone_number_id}/whatsapp-status User Key

Check WhatsApp status

Check the WhatsApp verification status of a phone number. Possible statuses: none, pending, active, or failed.
NameInTypeRequiredDescription
phone_number_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
numberstringrequired
whatsapp_statusstringrequired
Example Response
{
  "id": 5,
  "number": "+27211234567",
  "whatsapp_status": "active"
}
StatusDescription
404The phone number does not exist or belongs to a different company.
422Validation Error

Conversations

A conversation is a single interaction session between an end user and an agent -- either a voice call or a WhatsApp chat. Includes duration, transcript, and usage data.

GET /api/conversations/ User Key

List conversations

List conversations with cursor-based pagination. Filter by agent, channel (voice/whatsapp), and control page size. Returns conversation summaries without full transcripts.
NameInTypeRequiredDescription
agent_idquerystringoptional
channelquerystringoptional
limitqueryintegeroptional Default: 30
cursorquerystringoptional
FieldTypeRequiredDescription
itemsarray[object]required
has_morebooleanrequired
next_cursorstring?optional
Example Response
[
  {
    "id": 100,
    "agent_id": 10,
    "company_id": 1,
    "channel": "voice",
    "caller_number": "+27821234567",
    "direction": "inbound",
    "started_at": "2026-03-12T16:05:00Z",
    "ended_at": "2026-03-12T16:08:32Z",
    "duration_seconds": 212,
    "message_count": 14,
    "status": "completed",
    "call_successful": true,
    "transcript_summary": "Customer enquired about Pro plan pricing. Agent provided details and offered to send a follow-up email.",
    "source": "elevenlabs",
    "external_conversation_id": "el_conv_abc123",
    "created_at": "2026-03-12T16:05:00Z"
  }
]
StatusDescription
422Validation Error
GET /api/conversations/{conversation_id} User Key

Get conversation details

Retrieve the full details of a conversation, including the complete transcript and metadata. Only available for conversations belonging to your company.
NameInTypeRequiredDescription
conversation_idpathintegerrequired
FieldTypeRequiredDescription
idintegerrequired
agent_idintegerrequired
company_idintegerrequired
channelstringrequired
caller_numberstring?optional
directionstringrequired
started_atstringrequired
ended_atstring?optional
duration_secondsinteger?optional
message_countinteger?optional
statusstringrequired
call_successfulboolean?optional
sip_refer_destinationstring?optional
transcript_summarystring?optional
sourcestringrequired
external_conversation_idstring?optional
created_atstringrequired
transcriptarray?optional
metadata_object?optional
Example Response
{
  "id": 100,
  "agent_id": 10,
  "company_id": 1,
  "channel": "voice",
  "caller_number": "+27821234567",
  "direction": "inbound",
  "started_at": "2026-03-12T16:05:00Z",
  "ended_at": "2026-03-12T16:08:32Z",
  "duration_seconds": 212,
  "message_count": 14,
  "status": "completed",
  "call_successful": true,
  "transcript_summary": "Customer enquired about Pro plan pricing. Agent provided details and offered to send a follow-up email.",
  "source": "elevenlabs",
  "external_conversation_id": "el_conv_abc123",
  "created_at": "2026-03-12T16:05:00Z",
  "transcript": [
    {
      "role": "agent",
      "message": "Hi there! Thanks for calling Acme Corp. How can I help you today?",
      "timestamp": 0.0
    },
    {
      "role": "user",
      "message": "Hi, I was wondering about your pricing for the Pro plan.",
      "timestamp": 3.2
    },
    {
      "role": "agent",
      "message": "Great question! Our Pro plan starts at R499 per month and includes...",
      "timestamp": 5.8
    }
  ],
  "metadata_": {
    "llm_tokens": 1240
  }
}
StatusDescription
404The conversation does not exist or belongs to a different company.
422Validation Error

Usage

Track voice minutes, WhatsApp messages, and SIP refer minutes aggregated by company, agent, and date range.

GET /api/usage/summary Any Key

Get usage summary

Get aggregated usage metrics for your company over a date range. Returns total voice minutes, WhatsApp messages, and SIP refer minutes broken down by channel.
NameInTypeRequiredDescription
start_datequerystringoptional
end_datequerystringoptional
FieldTypeRequiredDescription
periodobjectrequired
voiceobjectrequired
agents_activeintegeroptional Default: 0
by_agentarray[AgentUsageBreakdown]optional Default: []
Example Response
{
  "period": {
    "start_date": "2026-03-01",
    "end_date": "2026-03-12"
  },
  "voice": {
    "total_conversations": 342,
    "total_minutes": 1205.7,
    "total_sip_refer_minutes": 87.3
  },
  "whatsapp": {
    "total_conversations": 156,
    "total_messages": 2840
  }
}
StatusDescription
422Validation Error
GET /api/usage/daily Any Key

Get daily usage breakdown

Get usage metrics broken down by day. Both start_date and end_date are required. Optionally filter to a specific agent.
NameInTypeRequiredDescription
start_datequerystringrequiredStart date
end_datequerystringrequiredEnd date
agent_idquerystringoptional
FieldTypeRequiredDescription
datestringrequired
voiceobjectrequired
Example Response
[
  {
    "date": "2026-03-11",
    "voice": {
      "total_conversations": 31,
      "total_minutes": 112.4,
      "total_sip_refer_minutes": 8.2
    },
    "whatsapp": {
      "total_conversations": 18,
      "total_messages": 245
    }
  },
  {
    "date": "2026-03-12",
    "voice": {
      "total_conversations": 47,
      "total_minutes": 156.3,
      "total_sip_refer_minutes": 12.5
    },
    "whatsapp": {
      "total_conversations": 23,
      "total_messages": 312
    }
  }
]
StatusDescription
422Validation Error

Webhooks

Register HTTPS endpoints to receive real-time events when conversations complete, agents change status, or WhatsApp verification finishes.

GET /api/webhooks/ Company Key

List webhooks

List all webhook registrations for your company.
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
urlstringrequired
eventsarray[object]required
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
[
  {
    "id": 1,
    "company_id": 1,
    "url": "https://your-app.com/webhooks/cxco",
    "events": [
      "conversation.completed",
      "agent.status_changed"
    ],
    "status": "active",
    "created_at": "2026-03-12T10:00:00Z",
    "updated_at": "2026-03-12T10:00:00Z"
  }
]
POST /api/webhooks/ Company Key

Register webhook

Register an HTTPS endpoint to receive real-time events. Optionally provide a shared secret for HMAC-SHA256 signature verification. Available events: conversation.completed, conversation.transferred, agent.status_changed, phone_number.whatsapp_verified.
Webhooks are delivered via POST with a JSON payload. Failed deliveries are retried with exponential backoff (10s, 30s, 90s, 270s, 810s). Your endpoint must respond within 10 seconds with a 2xx status.
FieldTypeRequiredDescription
urlstringrequired
eventsarray[string]required
secretstring?optional
Example
{
  "url": "https://your-app.com/webhooks/cxco",
  "events": [
    "conversation.completed",
    "agent.status_changed"
  ],
  "secret": "whsec_your_secret_key"
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
urlstringrequired
eventsarray[object]required
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 1,
  "company_id": 1,
  "url": "https://your-app.com/webhooks/cxco",
  "events": [
    "conversation.completed",
    "agent.status_changed"
  ],
  "status": "active",
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-12T10:00:00Z"
}
StatusDescription
422Validation Error
PUT /api/webhooks/{webhook_id} Company Key

Update webhook

Update a webhook's URL, subscribed events, or status. Only provided fields are updated.
NameInTypeRequiredDescription
webhook_idpathintegerrequired
FieldTypeRequiredDescription
urlstring?optional
eventsarray?optional
statusstring?optional
Example
{
  "url": "https://your-app.com/webhooks/cxco-v2",
  "events": [
    "conversation.completed",
    "conversation.transferred",
    "agent.status_changed"
  ]
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
urlstringrequired
eventsarray[object]required
statusstringrequired
created_atstringrequired
updated_atstringrequired
Example Response
{
  "id": 1,
  "company_id": 1,
  "url": "https://your-app.com/webhooks/cxco-v2",
  "events": [
    "conversation.completed",
    "conversation.transferred",
    "agent.status_changed"
  ],
  "status": "active",
  "created_at": "2026-03-12T10:00:00Z",
  "updated_at": "2026-03-13T09:00:00Z"
}
StatusDescription
422Validation Error
DELETE /api/webhooks/{webhook_id} Company Key

Delete webhook

Permanently delete a webhook registration. Events will no longer be delivered to this endpoint. This action is immediate and cannot be undone.
NameInTypeRequiredDescription
webhook_idpathintegerrequired
Example Response
"204 No Content"
StatusDescription
422Validation Error

Providers

View available service providers (voice, SIP, messaging) and securely store your provider credentials. Credentials are encrypted at rest with AES-256.

GET /api/providers/ Company Key

List providers

List all available service providers (voice AI, SIP trunk, messaging). Each provider has a unique slug used when storing credentials.
FieldTypeRequiredDescription
idintegerrequired
slugstringrequired
namestringrequired
typestringrequired
statusstringrequired
configobject?optional
Example Response
[
  {
    "id": 1,
    "slug": "elevenlabs",
    "name": "ElevenLabs",
    "type": "voice_provider",
    "status": "active"
  },
  {
    "id": 2,
    "slug": "sip_trunk",
    "name": "SIP Trunk Provider",
    "type": "sip_trunk",
    "status": "active"
  }
]
POST /api/providers/{slug}/credentials Company Key

Store provider credentials

Securely store your credentials for a service provider. Credentials are encrypted at rest using AES-256 (Fernet). Each company can store one set of credentials per provider.
Credentials are encrypted and cannot be read back. To update credentials, delete the existing record and create a new one.
NameInTypeRequiredDescription
slugpathstringrequired
FieldTypeRequiredDescription
company_idintegerrequired
credentialsobjectrequired
Example
{
  "company_id": 1,
  "credentials": {
    "api_key": "sk-elevenlabs-abc123..."
  }
}
FieldTypeRequiredDescription
idintegerrequired
company_idintegerrequired
provider_idintegerrequired
statusstringrequired
created_atstringrequired
Example Response
{
  "id": 1,
  "company_id": 1,
  "provider_id": 1,
  "status": "active",
  "created_at": "2026-03-12T10:00:00Z"
}
StatusDescription
409Credentials already exist for this company and provider combination.
422Validation Error