Phone numbers, calls, click-to-call, WebRTC webphones, recording, and real-time events — the developer guide to integrating Kavkom Voice into your application.
Introduction
The Kavkom API lets you add telephony features to your product: order phone numbers and lines, start and monitor calls, embed a phone directly in the browser, and receive real-time call events.
API building blocks
| Component | Base | Role |
|---|---|---|
| Billing API | client.kavkom.com | Order extensions, also called lines, and DID numbers. |
| PBX API | api.kavkom.com | List extensions, start calls, hang up calls. |
| WebSocket | live.kavkom.com | Receive the status of inbound and outbound calls in real time. |
| Webhooks | Your HTTPS endpoint | Receive live calls and CDRs on a URL from your system. |
Base URLs & conventions
All requests and responses use JSON. Always send the Content-Type: application/json and Accept: application/json headers.
| API | Base URL | Response envelope |
|---|---|---|
| Billing | https://client.kavkom.com | { "status": "success", "return": { … } } |
| PBX | https://api.kavkom.com | { "data": { … }, "success": true, "message": "…" } |
| WebSocket | wss://live.kavkom.com | JSON messages { "event": "…", "data": { … } } |
Authentication
API token X-API-TOKEN & domain_uuid
The Billing and PBX APIs use an API token placed in the X-API-TOKEN header, together with a domain_uuid that identifies the customer domain.
X-API-TOKEN: <your-api-token>
- The token is linked to one or more domains and to a list of authorized routes.
domain_uuidis a UUID v4. It is sent in the query string for aGETrequest and in the JSON body for aPOSTrequest.- A token that is not authorized for the requested route, or not linked to the supplied
domain_uuid, is rejected.
Create an API key
Here is how to create an API key:
- Log in to the Kavkom application interface with an administrator account, then open the advanced settings by clicking the gear icon at the bottom left (1).
- Click the API Settings tab (2), then click the Add Token button (3).
- Enter the Token name (1), enable domain access authorization and keep the
domain_uuid(2). - Select the objects to authorize from the list on the right (3), then save by clicking Create (4).
- Once you are back on the API Tokens list, copy and store the key by clicking the Copy button in the Actions column.
Access tokens for the WebSocket
The /calls WebSocket connection uses a short-lived access token obtained from a refresh token on the server side.
- Obtain a refresh token with a protected Bearer token on the server side.
- Exchange it for an access token with the
callsscope and the authorized extensions.
POST https://live.kavkom.com/api/auth/acquire-access
Authorization: Bearer <refresh-token>
Content-Type: application/json
{
"userUuid": "<user-uuid>",
"extensions": [1001],
"scope": "calls",
"application": "my-app"
}
Response:
{ "token": "<access-token>", "expiresAt": "<ISO-date>" }
| Endpoint | Role |
|---|---|
POST /api/auth/acquire-access | Obtain an access token from the refresh token. |
POST /api/auth/verify-access | Verify and optionally extend an access token. |
POST /api/auth/revoke-access | Revoke an access token. |
POST /api/auth/revoke-refresh | Revoke the refresh token. |
Ordering resources – Billing API
Extensions and numbers are ordered through the billing system, which creates the order, handles billing and then automatically provisions the resource on the PBX. Extensions are not created directly on the PBX.
Order a line / extension
POST https://client.kavkom.com/api.php with route=order and type=line
| Field | Required | Description |
|---|---|---|
route | yes | Must be order. |
type | yes | Must be line. |
domain_uuid | yes | UUID v4 of the customer domain. |
options | yes | Map of option name → quantity, for example { "starter": 2 }. |
curl -X POST "https://client.kavkom.com/api.php" \
-H "X-API-TOKEN: <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"route": "order",
"type": "line",
"domain_uuid": "ddcfeb62-110f-4bd7-b7a9-965e1a78295f",
"options": { "starter": 2 }
}'
Successful response:
{ "invoiceid": 10231, "order_number": "0123456789" }
Order a DID number
The order is registered and, once the customer’s identity has been validated, a number is allocated from the stock and provisioned on the PBX with routing to the selected extension.
POST https://client.kavkom.com/api.php with route=order and type=did
| Field | Required | Description |
|---|---|---|
route | yes | Must be order. |
type | yes | Must be did. |
domain_uuid | yes | UUID v4 of the customer domain. |
country | yes | Country available in the DID catalog. |
location | yes | Available city or locality. |
extension | yes | Extension to route inbound calls to. |
description | no | Free label stored with the order. |
identiy | no | Identity registration identifier. The parameter spelling is intentionally identiy. |
curl -X POST "https://client.kavkom.com/api.php" \
-H "X-API-TOKEN: <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"route": "order",
"type": "did",
"domain_uuid": "ddcfeb62-110f-4bd7-b7a9-965e1a78295f",
"country": "FR",
"location": "Paris",
"extension": "901",
"description": "Sales line"
}'
{
"status": "success",
"return": { "id": 152, "accepted": true }
}
{ "status": "error", "message": "…" }. Instant provisioning is therefore not guaranteed in all cases.List number orders
GET https://client.kavkom.com/api.php with route=order and type=didlist
Required parameter: domain_uuid. Optional parameters: orderby with order_date or accept_date, and asc.
Managing calls – PBX API
List extensions
Returns the extensions configured for a domain, with pagination. A lightweight variant is also available: GET /api/pbx/v1/extension/list_limited.
GET https://api.kavkom.com/api/pbx/v1/extension/list
| Parameter | Required | Description |
|---|---|---|
domain_uuid | yes | UUID v4 of the domain. |
limit | no | Records per page, from 1 to 5000. Default: 15. |
page | no | Page number. |
filter[search_query] | no | Filter text, for example an extension number. |
sort[0] | no | Column: extension, effective_caller_id_number, outbound_caller_id_name, description. |
sort[1] | no | Direction: ASC or DESC. |
with_user | no | 1 to include the linked user, 0 to omit it. |
curl -X GET "https://api.kavkom.com/api/pbx/v1/extension/list?domain_uuid=ddcfeb62-110f-4bd7-b7a9-965e1a78295f" \
-H "X-API-TOKEN: <token>" \
-H "Accept: application/json"
{
"data": [
{
"extension_uuid": "48b0d200-2b31-4211-a3d7-b41f87abbcbb",
"domain_uuid": "ddcfeb62-110f-4bd7-b7a9-965e1a78295f",
"extension": "901",
"password": "aB3xY9zK",
"user_context": "client.kavkom.com",
"enabled": "true"
}
],
"success": true,
"message": "Action completed successfully"
}
Start a call, or click-to-call
Originates a call: the PBX first calls the source extension, then bridges it to the destination. The call is automatically recorded and its call_uuid is returned.
POST https://api.kavkom.com/api/pbx/v1/active_call/call
| Parameter | Required | Description |
|---|---|---|
domain_uuid | yes | UUID v4 of the domain. |
src | yes | Source extension. Must exist on the domain. |
destination | yes | Number to call: internal extension, external number or SIP URI. |
auto_answer | no | "true" to automatically answer the source leg. |
ignore_early | no | Hang up after bridging and ignore early media. |
src_cid_number | no | Caller ID displayed on the source leg. |
curl -X POST "https://api.kavkom.com/api/pbx/v1/active_call/call" \
-H "X-API-TOKEN: <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"domain_uuid": "ddcfeb62-110f-4bd7-b7a9-965e1a78295f",
"src": "901",
"destination": "33612345678",
"auto_answer": "true"
}'
{
"success": true,
"message": "Action completed successfully",
"call_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recording": "+OK"
}
src, rings first and is then connected to the customer.Hang up a call
POST https://api.kavkom.com/api/pbx/v1/active_call/kill
Expected body: domain_uuid and call_uuid, where call_uuid is returned by the origination endpoint.
Register an extension with SIP
Once the extension has been ordered and provisioned, any SIP device or softphone can register on the PBX. The registration, or REGISTER, tells the switch where the user can be reached. It is required before making or receiving calls.
Credentials
| SIP setting | Value | Source field |
|---|---|---|
| Username / Auth user | The extension number, e.g. 901 | extension |
| Password / Secret | The SIP secret | password |
| Domain / Realm / SIP server | The domain name, e.g. client.kavkom.com | user_context |
| Transport | UDP, TCP or TLS. Prefer TLS. | Device setting |
| Port | 5060 UDP/TCP or 5061 TLS | Device setting |
user_context. This is how the PBX determines the device domain.Registration flow
- The device sends a
REGISTERrequest to the registrar withusername@domain. - The PBX replies with
401 Unauthorizedand a digest challenge. - The device sends another
REGISTERwith the computed digest. - On success, the switch returns
200 OKand stores the contact.
SIP Username : 901
Password : aB3xY9zK
SIP Domain : client.kavkom.com
Registrar : client.kavkom.com
Transport : TLS
Port : 5061
Expiry : 3600
403 Forbidden on REGISTER generally means an incorrect password or a realm that differs from user_context. Behind NAT, enable keep-alive to remain reachable.Build a WebRTC webphone application with SIP.js
This section shows how to embed a phone directly in the browser using an extension’s credentials. The reference implementation uses SIP.js and its SimpleUser helper.
Prerequisites
| Item | Details |
|---|---|
| API token | Authorized for your domain and for the extension list endpoint. |
domain_uuid | UUID v4 of the customer domain. |
| Provisioned extension | An extension must already exist. |
| Modern browser | Microphone access is required; the page must be served over HTTPS. |
Step 1 – Retrieve SIP credentials on the server side
Call the extension list endpoint to read the three values required for registration: extension, password and user_context, which corresponds to the domain or realm. Check that the extension is enabled: "true".
Steps 2 & 3 – Register and call from the browser
import { Web } from "sip.js";
// 1) Retrieve credentials from YOUR backend.
// Never call the Kavkom API with the token directly from the browser.
const { extension, password, user_context: domain } = await fetch(
"/api/my-backend/sip-credentials"
).then((r) => r.json());
// 2) Build the SIP client from those values.
const sipTarget = (number) => `sip:${number}@${domain}`;
const simpleUser = new Web.SimpleUser(`wss://${domain}/`, {
aor: sipTarget(extension),
media: { remote: { audio: document.querySelector("#remoteAudio") } },
userAgentOptions: {
authorizationUsername: extension,
authorizationPassword: password,
},
delegate: {
onServerConnect: () => simpleUser.register(),
onCallReceived: () => simpleUser.answer(),
},
});
// 3) Connect + register, then place calls.
await simpleUser.connect(); // triggers onServerConnect -> register()
// await simpleUser.call(sipTarget("33612345678"));
// await simpleUser.hangup();
During a call, the client can handle hold, mute, transfer and the DTMF keypad.
Variant – Originate from the server
Instead of letting the client dial, you can start the call from your backend with the origination endpoint. This is useful for a click-to-call button where the user’s extension should ring first.
The connection fails immediately
The domain is wrong: it must match user_context and be reachable at wss://<domain>/. The page must be served over HTTPS.
The extension registers, then disconnects
Check the password and make sure the realm matches user_context.
There is no audio
Microphone permission was probably denied, or no <audio> element is linked to the remote media.
Calls cannot be placed
Check that the extension is enabled: "true" and that the destination format is correct.
Real-time call events – WebSocket
The /calls WebSocket pushes call status in real time for the requested extensions.
Connection
WSS wss://live.kavkom.com/calls?t=<access-token>&ext=1001
| Parameter | Description |
|---|---|
t | Access token obtained via acquire-access with the calls scope. |
ext | Comma-separated list of extensions, for example 1001 or 1001,1002. |
- Missing
t: the server returns406, “&t parameter is missing”. - Missing
ext:406, “&ext parameter is missing”. - Invalid or expired token: connection rejected with
401orInvalid token.
Received messages
| Event | Description |
|---|---|
call | Call state update: ringing, active, held, finished. |
ping | Heartbeat; the client must reply with pong. |
error | Error message in data.message. |
call event payload
{
"event": "call",
"data": {
"callUuid": "abc-123",
"state": "active",
"direction": "inbound",
"number": "380501234567",
"presenceId": "1001@contact.example.kavkom.com",
"createdEpoch": 1710763200,
"answeredEpoch": 1710763205,
"duration": 120
}
}
Ping / Pong
The server sends ping with data: { sentStamp, expiresInSec }. The client must return a pong with the same structure.
{ "event": "pong", "data": { "sentStamp": 1710763200000, "expiresInSec": 60 } }
JavaScript client example
const ws = new WebSocket(
`wss://live.kavkom.com/calls?t=${token}&ext=1001`
);
ws.onmessage = (msg) => {
const { event, data } = JSON.parse(msg.data);
if (event === "call") console.log(data.state, data.direction, data.number);
if (event === "ping") ws.send(JSON.stringify({ event: "pong", data }));
};
Webhooks – pushing calls & CDRs
From the Kavkom interface, under Advanced → Integrations → Webhook, configure a URL to which Kavkom will send call events. This mechanism is useful for screen-pop, CRM logging, workflow triggering or real-time dashboards.
Configuration parameters
| Field | Description |
|---|---|
| Endpoint | HTTPS URL of your server. A test button lets you validate it. |
| Headers | Custom headers, for example Authorization: Bearer …. |
| Variables | Custom variables added to the payload. |
| Call types | Live inbound calls, live outbound calls, and/or CDRs after the call ends. |
| Mapping | Mapping between Kavkom fields and the fields expected by your system. |
Example received payload
POST /webhooks/calls HTTP/1.1
Authorization: Bearer <your-key>
Content-Type: application/json
{
"event": "cdr",
"callUuid": "a1b2c3d4-...-ef1234567890",
"direction": "outbound",
"number": "33612345678",
"state": "finished",
"duration": 120
}
Endpoint reference
| Method & path | API | Role |
|---|---|---|
POST /api.php (route=order, type=line) | Billing | Order a line / extension. |
POST /api.php (route=order, type=did) | Billing | Order a DID number. |
GET /api.php (route=order, type=didlist) | Billing | List DID orders. |
GET /api/pbx/v1/extension/list | PBX | List extensions. |
GET /api/pbx/v1/extension/list_limited | PBX | Lightweight extension list. |
POST /api/pbx/v1/active_call/call | PBX | Start a call. |
POST /api/pbx/v1/active_call/kill | PBX | Hang up a call. |
POST /api/auth/acquire-access | Auth | Get a WebSocket access token. |
POST /api/auth/verify-access | Auth | Verify or extend the token. |
POST /api/auth/revoke-access | Auth | Revoke the access token. |
WSS /calls?t=&ext= | WebSocket | Call event stream. |
Error codes & troubleshooting
Error format
Billing API:
{ "status": "error", "message": "Fields required: domain_uuid, options" }
PBX API:
{
"error": true,
"message": "Model is not valid",
"validationErrors": { "extension": "The extension field is required." }
}
| Case | Meaning / action |
|---|---|
422 + “src invalid” | The source extension does not exist on this domain. |
422 + switch text -ERR … | The call was rejected by the switch. |
| Rejected token | The token is not authorized for the route, or is not linked to the supplied domain_uuid. |
406 WebSocket | Missing t or ext parameter. |
401 WebSocket | Invalid or expired token. |
403 SIP REGISTER | Incorrect password or realm different from user_context. |
Glossary
| Term | Definition |
|---|---|
| DID | Direct Inward Dialing: a phone number that can be reached directly. |
| Extension / line | Telephone endpoint on the PBX, identified by a short number such as 901. |
domain_uuid | UUID v4 identifying the customer, or tenant, on the platform. |
| CDR | Call Detail Record: metadata for a completed call. |
| IVR | Interactive voice response, or touch-tone menu. |
| SIP | Session initiation protocol for voice over IP. |
| WebRTC | Real-time communication technology in the browser. |
| Bridge | Connection of two call legs. |