{"id":4004,"date":"2022-05-23T18:05:51","date_gmt":"2022-05-23T16:05:51","guid":{"rendered":"https:\/\/elegant-margulis.82-165-125-94.plesk.page\/getting-started\/kavkom-api\/"},"modified":"2026-06-03T14:25:08","modified_gmt":"2026-06-03T12:25:08","slug":"kavkom-api","status":"publish","type":"post","link":"https:\/\/help.kavkom.com\/en\/api-integration\/kavkom-api\/","title":{"rendered":"Kavkom API Documentation"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Phone numbers, calls, click-to-call, WebRTC webphones, recording, and real-time events \u2014 the developer guide to integrating Kavkom Voice into your application.<\/p>\n\n\n\n<div class=\"kavkom-api-doc\" id=\"kavkom-api-doc-en\">\n  <style>\/* Kavkom Help Center API documentation - scoped for Elementor HTML widget *\/\n.kavkom-api-doc {\n  --kavkom-blue: #1f6fe5;\n  --kavkom-blue-dark: #0f3764;\n  --kavkom-ink: #111827;\n  --kavkom-muted: #5b677a;\n  --kavkom-line: #d9e2ef;\n  --kavkom-soft: #f5f8fc;\n  --kavkom-code: #0f172a;\n  --kavkom-info: #edf5ff;\n  --kavkom-warning: #fff7df;\n  --kavkom-success: #ecfdf3;\n  color: var(--kavkom-ink);\n  font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Arial, sans-serif;\n  line-height: 1.65;\n  max-width: 1120px;\n  margin: 0 auto;\n}\n.kavkom-api-doc * { box-sizing: border-box; }\n.kavkom-api-doc a { color: var(--kavkom-blue); text-decoration: none; }\n.kavkom-api-doc a:hover { text-decoration: underline; }\n.kavkom-api-doc .kdoc-hero {\n  background: linear-gradient(135deg, #0f3764 0%, #1f6fe5 100%);\n  color: #fff;\n  border-radius: 24px;\n  padding: 42px;\n  margin: 0 0 28px;\n  box-shadow: 0 18px 50px rgba(15, 55, 100, .18);\n}\n.kavkom-api-doc .kdoc-eyebrow { text-transform: uppercase; letter-spacing: .12em; font-size: 12px; font-weight: 800; opacity: .86; margin-bottom: 10px; }\n.kavkom-api-doc h1 { font-size: clamp(34px, 5vw, 56px); line-height: 1.04; margin: 0 0 14px; letter-spacing: -.04em; }\n.kavkom-api-doc .kdoc-hero p { max-width: 820px; font-size: 18px; margin: 0; opacity: .94; }\n.kavkom-api-doc .kdoc-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }\n.kavkom-api-doc .kdoc-pill { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px; border-radius: 999px; background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.2); font-size: 13px; font-weight: 700; }\n.kavkom-api-doc .kdoc-layout { display: block; }\n.kavkom-api-doc .kdoc-content { min-width: 0; }\n.kavkom-api-doc section { margin-bottom: 34px; }\n.kavkom-api-doc h2 { color: var(--kavkom-blue-dark); font-size: clamp(25px, 3vw, 34px); line-height: 1.15; margin: 0 0 14px; letter-spacing: -.025em; border-bottom: 2px solid var(--kavkom-line); padding-bottom: 12px; }\n.kavkom-api-doc h3 { color: var(--kavkom-blue-dark); font-size: 22px; line-height: 1.25; margin: 28px 0 10px; }\n.kavkom-api-doc h4 { color: var(--kavkom-ink); font-size: 17px; margin: 22px 0 8px; }\n.kavkom-api-doc p { margin: 10px 0; }\n.kavkom-api-doc .kdoc-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; margin: 18px 0; }\n.kavkom-api-doc .kdoc-card { background: var(--kavkom-soft); border: 1px solid var(--kavkom-line); border-radius: 18px; padding: 18px; }\n.kavkom-api-doc .kdoc-card h3, .kavkom-api-doc .kdoc-card h4 { margin-top: 0; }\n.kavkom-api-doc .kdoc-alert { border-radius: 16px; padding: 16px 18px; margin: 16px 0; border-left: 5px solid var(--kavkom-blue); background: var(--kavkom-info); }\n.kavkom-api-doc .kdoc-alert--warning { background: var(--kavkom-warning); border-left-color: #f59e0b; }\n.kavkom-api-doc .kdoc-alert--success { background: var(--kavkom-success); border-left-color: #22c55e; }\n.kavkom-api-doc code { background: #f1f5f9; color: #b64242; border-radius: 6px; padding: 2px 6px; font-size: .92em; }\n.kavkom-api-doc pre { background: var(--kavkom-code); color: #e5edf7; border-radius: 16px; padding: 18px; overflow-x: auto; margin: 16px 0; font-size: 13.5px; line-height: 1.55; box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); }\n.kavkom-api-doc pre code { background: transparent; color: inherit; padding: 0; border-radius: 0; font-size: inherit; }\n.kavkom-api-doc .kdoc-table-wrap { overflow-x: auto; margin: 16px 0; border: 1px solid var(--kavkom-line); border-radius: 16px; }\n.kavkom-api-doc table { width: 100%; border-collapse: collapse; min-width: 680px; background: #fff; }\n.kavkom-api-doc th { background: #eaf2fb; color: var(--kavkom-blue-dark); text-align: left; font-weight: 800; }\n.kavkom-api-doc th, .kavkom-api-doc td { padding: 12px 14px; border-bottom: 1px solid var(--kavkom-line); vertical-align: top; }\n.kavkom-api-doc tr:last-child td { border-bottom: 0; }\n.kavkom-api-doc .kdoc-method { display: inline-block; min-width: 54px; text-align: center; border-radius: 999px; padding: 4px 8px; color: #fff; font-size: 12px; font-weight: 900; letter-spacing: .03em; }\n.kavkom-api-doc .kdoc-get { background: #0ea5e9; }\n.kavkom-api-doc .kdoc-post { background: #1f6fe5; }\n.kavkom-api-doc .kdoc-wss { background: #7c3aed; }\n.kavkom-api-doc .kdoc-endpoint { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace; font-size: 13px; }\n.kavkom-api-doc .kdoc-flow { counter-reset: step; margin: 18px 0; padding: 0; list-style: none; }\n.kavkom-api-doc .kdoc-flow li { counter-increment: step; position: relative; margin: 12px 0; padding: 16px 16px 16px 58px; border: 1px solid var(--kavkom-line); border-radius: 16px; background: #fff; }\n.kavkom-api-doc .kdoc-flow li:before { content: counter(step); position: absolute; left: 16px; top: 16px; width: 28px; height: 28px; border-radius: 50%; background: var(--kavkom-blue); color: #fff; display: grid; place-items: center; font-weight: 900; }\n.kavkom-api-doc details { border: 1px solid var(--kavkom-line); border-radius: 14px; padding: 14px 16px; margin: 10px 0; background: #fff; }\n.kavkom-api-doc summary { cursor: pointer; font-weight: 800; color: var(--kavkom-blue-dark); }\n.kavkom-api-doc .kdoc-footer { color: var(--kavkom-muted); font-size: 13px; text-align: center; margin: 32px 0 0; }\n@media (max-width: 920px) {\n  .kavkom-api-doc .kdoc-layout { grid-template-columns: 1fr; }\n  .kavkom-api-doc .kdoc-grid { grid-template-columns: 1fr; }\n  .kavkom-api-doc .kdoc-hero { padding: 24px; }\n}<\/style>\n\n  <div class=\"kdoc-layout\">\n    <main class=\"kdoc-content\">\n      <section id=\"en-introduction\">\n        <h2>Introduction<\/h2>\n        <p>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.<\/p>\n        <h3>API building blocks<\/h3>\n        <div class=\"kdoc-table-wrap\">\n          <table>\n            <thead><tr><th>Component<\/th><th>Base<\/th><th>Role<\/th><\/tr><\/thead>\n            <tbody>\n              <tr><td><strong>Billing API<\/strong><\/td><td><code>client.kavkom.com<\/code><\/td><td>Order extensions, also called lines, and DID numbers.<\/td><\/tr>\n              <tr><td><strong>PBX API<\/strong><\/td><td><code>api.kavkom.com<\/code><\/td><td>List extensions, start calls, hang up calls.<\/td><\/tr>\n              <tr><td><strong>WebSocket<\/strong><\/td><td><code>live.kavkom.com<\/code><\/td><td>Receive the status of inbound and outbound calls in real time.<\/td><\/tr>\n              <tr><td><strong>Webhooks<\/strong><\/td><td>Your HTTPS endpoint<\/td><td>Receive live calls and CDRs on a URL from your system.<\/td><\/tr>\n            <\/tbody>\n          <\/table>\n        <\/div>\n        <h3>Base URLs &#038; conventions<\/h3>\n        <p>All requests and responses use JSON. Always send the <code>Content-Type: application\/json<\/code> and <code>Accept: application\/json<\/code> headers.<\/p>\n        <div class=\"kdoc-table-wrap\">\n          <table>\n            <thead><tr><th>API<\/th><th>Base URL<\/th><th>Response envelope<\/th><\/tr><\/thead>\n            <tbody>\n              <tr><td>Billing<\/td><td><code>https:\/\/client.kavkom.com<\/code><\/td><td><code>{ \"status\": \"success\", \"return\": { \u2026 } }<\/code><\/td><\/tr>\n              <tr><td>PBX<\/td><td><code>https:\/\/api.kavkom.com<\/code><\/td><td><code>{ \"data\": { \u2026 }, \"success\": true, \"message\": \"\u2026\" }<\/code><\/td><\/tr>\n              <tr><td>WebSocket<\/td><td><code>wss:\/\/live.kavkom.com<\/code><\/td><td>JSON messages <code>{ \"event\": \"\u2026\", \"data\": { \u2026 } }<\/code><\/td><\/tr>\n            <\/tbody>\n          <\/table>\n        <\/div>\n      <\/section>\n\n      <section id=\"en-auth\">\n        <h2>Authentication<\/h2>\n        <h3>API token <code>X-API-TOKEN<\/code> &#038; <code>domain_uuid<\/code><\/h3>\n        <p>The Billing and PBX APIs use an API token placed in the <code>X-API-TOKEN<\/code> header, together with a <code>domain_uuid<\/code> that identifies the customer domain.<\/p>\n        <pre><code>X-API-TOKEN: &lt;your-api-token&gt;<\/code><\/pre>\n        <ul>\n          <li>The token is linked to one or more domains and to a list of authorized routes.<\/li>\n          <li><code>domain_uuid<\/code> is a UUID v4. It is sent in the query string for a <code>GET<\/code> request and in the JSON body for a <code>POST<\/code> request.<\/li>\n          <li>A token that is not authorized for the requested route, or not linked to the supplied <code>domain_uuid<\/code>, is rejected.<\/li>\n        <\/ul>\n        <div class=\"kdoc-alert kdoc-alert--success kdoc-api-key\">\n          <h4>Create an API key<\/h4>\n          <p>Here is how to create an API key:<\/p>\n          <ol>\n            <li>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 <strong>(1)<\/strong>.<\/li>\n            <li>Click the <strong>API Settings<\/strong> tab <strong>(2)<\/strong>, then click the <strong>Add Token<\/strong> button <strong>(3)<\/strong>.<\/li>\n            <li>Enter the Token name <strong>(1)<\/strong>, enable domain access authorization and keep the <code>domain_uuid<\/code> <strong>(2)<\/strong>.<\/li>\n            <li>Select the objects to authorize from the list on the right <strong>(3)<\/strong>, then save by clicking <strong>Create<\/strong> <strong>(4)<\/strong>.<\/li>\n            <li>Once you are back on the API Tokens list, copy and store the key by clicking the <strong>Copy<\/strong> button in the <strong>Actions<\/strong> column.<\/li>\n          <\/ol>\n        <\/div>\n        <h3>Access tokens for the WebSocket<\/h3>\n        <p>The <code>\/calls<\/code> WebSocket connection uses a short-lived access token obtained from a refresh token on the server side.<\/p>\n        <ol class=\"kdoc-flow\">\n          <li>Obtain a refresh token with a protected Bearer token on the server side.<\/li>\n          <li>Exchange it for an access token with the <code>calls<\/code> scope and the authorized extensions.<\/li>\n        <\/ol>\n        <pre><code>POST https:\/\/live.kavkom.com\/api\/auth\/acquire-access\nAuthorization: Bearer &lt;refresh-token&gt;\nContent-Type: application\/json\n\n{\n  \"userUuid\": \"&lt;user-uuid&gt;\",\n  \"extensions\": [1001],\n  \"scope\": \"calls\",\n  \"application\": \"my-app\"\n}<\/code><\/pre>\n        <p>Response:<\/p>\n        <pre><code>{ \"token\": \"&lt;access-token&gt;\", \"expiresAt\": \"&lt;ISO-date&gt;\" }<\/code><\/pre>\n        <div class=\"kdoc-table-wrap\">\n          <table>\n            <thead><tr><th>Endpoint<\/th><th>Role<\/th><\/tr><\/thead>\n            <tbody>\n              <tr><td><code>POST \/api\/auth\/acquire-access<\/code><\/td><td>Obtain an access token from the refresh token.<\/td><\/tr>\n              <tr><td><code>POST \/api\/auth\/verify-access<\/code><\/td><td>Verify and optionally extend an access token.<\/td><\/tr>\n              <tr><td><code>POST \/api\/auth\/revoke-access<\/code><\/td><td>Revoke an access token.<\/td><\/tr>\n              <tr><td><code>POST \/api\/auth\/revoke-refresh<\/code><\/td><td>Revoke the refresh token.<\/td><\/tr>\n            <\/tbody>\n          <\/table>\n        <\/div>\n        <div class=\"kdoc-alert kdoc-alert--warning\"><strong>Security.<\/strong> Never expose the API token in the browser. The token must remain on your backend. The front end asks your server for the required data, and your server calls the Kavkom API.<\/div>\n      <\/section>\n\n      <section id=\"en-billing\">\n        <h2>Ordering resources &#8211; Billing API<\/h2>\n        <p>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.<\/p>\n        <h3>Order a line \/ extension<\/h3>\n        <p><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>https:\/\/client.kavkom.com\/api.php<\/code> with <code>route=order<\/code> and <code>type=line<\/code><\/p>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Field<\/th><th>Required<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>route<\/code><\/td><td>yes<\/td><td>Must be <code>order<\/code>.<\/td><\/tr>\n          <tr><td><code>type<\/code><\/td><td>yes<\/td><td>Must be <code>line<\/code>.<\/td><\/tr>\n          <tr><td><code>domain_uuid<\/code><\/td><td>yes<\/td><td>UUID v4 of the customer domain.<\/td><\/tr>\n          <tr><td><code>options<\/code><\/td><td>yes<\/td><td>Map of <code>option name \u2192 quantity<\/code>, for example <code>{ \"starter\": 2 }<\/code>.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <pre><code>curl -X POST \"https:\/\/client.kavkom.com\/api.php\" \\\n  -H \"X-API-TOKEN: &lt;token&gt;\" \\\n  -H \"Content-Type: application\/json\" \\\n  -H \"Accept: application\/json\" \\\n  -d '{\n    \"route\": \"order\",\n    \"type\": \"line\",\n    \"domain_uuid\": \"ddcfeb62-110f-4bd7-b7a9-965e1a78295f\",\n    \"options\": { \"starter\": 2 }\n  }'<\/code><\/pre>\n        <p>Successful response:<\/p>\n        <pre><code>{ \"invoiceid\": 10231, \"order_number\": \"0123456789\" }<\/code><\/pre>\n\n        <h3>Order a DID number<\/h3>\n        <p>The order is registered and, once the customer&#8217;s identity has been validated, a number is allocated from the stock and provisioned on the PBX with routing to the selected extension.<\/p>\n        <p><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>https:\/\/client.kavkom.com\/api.php<\/code> with <code>route=order<\/code> and <code>type=did<\/code><\/p>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Field<\/th><th>Required<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>route<\/code><\/td><td>yes<\/td><td>Must be <code>order<\/code>.<\/td><\/tr>\n          <tr><td><code>type<\/code><\/td><td>yes<\/td><td>Must be <code>did<\/code>.<\/td><\/tr>\n          <tr><td><code>domain_uuid<\/code><\/td><td>yes<\/td><td>UUID v4 of the customer domain.<\/td><\/tr>\n          <tr><td><code>country<\/code><\/td><td>yes<\/td><td>Country available in the DID catalog.<\/td><\/tr>\n          <tr><td><code>location<\/code><\/td><td>yes<\/td><td>Available city or locality.<\/td><\/tr>\n          <tr><td><code>extension<\/code><\/td><td>yes<\/td><td>Extension to route inbound calls to.<\/td><\/tr>\n          <tr><td><code>description<\/code><\/td><td>no<\/td><td>Free label stored with the order.<\/td><\/tr>\n          <tr><td><code>identiy<\/code><\/td><td>no<\/td><td>Identity registration identifier. The parameter spelling is intentionally <code>identiy<\/code>.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <pre><code>curl -X POST \"https:\/\/client.kavkom.com\/api.php\" \\\n  -H \"X-API-TOKEN: &lt;token&gt;\" \\\n  -H \"Content-Type: application\/json\" \\\n  -H \"Accept: application\/json\" \\\n  -d '{\n    \"route\": \"order\",\n    \"type\": \"did\",\n    \"domain_uuid\": \"ddcfeb62-110f-4bd7-b7a9-965e1a78295f\",\n    \"country\": \"FR\",\n    \"location\": \"Paris\",\n    \"extension\": \"901\",\n    \"description\": \"Sales line\"\n  }'<\/code><\/pre>\n        <pre><code>{\n  \"status\": \"success\",\n  \"return\": { \"id\": 152, \"accepted\": true }\n}<\/code><\/pre>\n        <div class=\"kdoc-alert\"><strong>KYC identity.<\/strong> If the country or locality is not available, or if the identity has not been validated, the response is <code>{ \"status\": \"error\", \"message\": \"\u2026\" }<\/code>. Instant provisioning is therefore not guaranteed in all cases.<\/div>\n\n        <h3>List number orders<\/h3>\n        <p><span class=\"kdoc-method kdoc-get\">GET<\/span> <code>https:\/\/client.kavkom.com\/api.php<\/code> with <code>route=order<\/code> and <code>type=didlist<\/code><\/p>\n        <p>Required parameter: <code>domain_uuid<\/code>. Optional parameters: <code>orderby<\/code> with <code>order_date<\/code> or <code>accept_date<\/code>, and <code>asc<\/code>.<\/p>\n      <\/section>\n\n      <section id=\"en-pbx\">\n        <h2>Managing calls &#8211; PBX API<\/h2>\n        <h3>List extensions<\/h3>\n        <p>Returns the extensions configured for a domain, with pagination. A lightweight variant is also available: <code>GET \/api\/pbx\/v1\/extension\/list_limited<\/code>.<\/p>\n        <p><span class=\"kdoc-method kdoc-get\">GET<\/span> <code>https:\/\/api.kavkom.com\/api\/pbx\/v1\/extension\/list<\/code><\/p>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Parameter<\/th><th>Required<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>domain_uuid<\/code><\/td><td>yes<\/td><td>UUID v4 of the domain.<\/td><\/tr>\n          <tr><td><code>limit<\/code><\/td><td>no<\/td><td>Records per page, from 1 to 5000. Default: 15.<\/td><\/tr>\n          <tr><td><code>page<\/code><\/td><td>no<\/td><td>Page number.<\/td><\/tr>\n          <tr><td><code>filter[search_query]<\/code><\/td><td>no<\/td><td>Filter text, for example an extension number.<\/td><\/tr>\n          <tr><td><code>sort[0]<\/code><\/td><td>no<\/td><td>Column: <code>extension<\/code>, <code>effective_caller_id_number<\/code>, <code>outbound_caller_id_name<\/code>, <code>description<\/code>.<\/td><\/tr>\n          <tr><td><code>sort[1]<\/code><\/td><td>no<\/td><td>Direction: <code>ASC<\/code> or <code>DESC<\/code>.<\/td><\/tr>\n          <tr><td><code>with_user<\/code><\/td><td>no<\/td><td><code>1<\/code> to include the linked user, <code>0<\/code> to omit it.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <pre><code>curl -X GET \"https:\/\/api.kavkom.com\/api\/pbx\/v1\/extension\/list?domain_uuid=ddcfeb62-110f-4bd7-b7a9-965e1a78295f\" \\\n  -H \"X-API-TOKEN: &lt;token&gt;\" \\\n  -H \"Accept: application\/json\"<\/code><\/pre>\n        <pre><code>{\n  \"data\": [\n    {\n      \"extension_uuid\": \"48b0d200-2b31-4211-a3d7-b41f87abbcbb\",\n      \"domain_uuid\": \"ddcfeb62-110f-4bd7-b7a9-965e1a78295f\",\n      \"extension\": \"901\",\n      \"password\": \"aB3xY9zK\",\n      \"user_context\": \"client.kavkom.com\",\n      \"enabled\": \"true\"\n    }\n  ],\n  \"success\": true,\n  \"message\": \"Action completed successfully\"\n}<\/code><\/pre>\n\n        <h3>Start a call, or click-to-call<\/h3>\n        <p>Originates a call: the PBX first calls the source extension, then bridges it to the destination. The call is automatically recorded and its <code>call_uuid<\/code> is returned.<\/p>\n        <p><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>https:\/\/api.kavkom.com\/api\/pbx\/v1\/active_call\/call<\/code><\/p>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Parameter<\/th><th>Required<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>domain_uuid<\/code><\/td><td>yes<\/td><td>UUID v4 of the domain.<\/td><\/tr>\n          <tr><td><code>src<\/code><\/td><td>yes<\/td><td>Source extension. Must exist on the domain.<\/td><\/tr>\n          <tr><td><code>destination<\/code><\/td><td>yes<\/td><td>Number to call: internal extension, external number or SIP URI.<\/td><\/tr>\n          <tr><td><code>auto_answer<\/code><\/td><td>no<\/td><td><code>\"true\"<\/code> to automatically answer the source leg.<\/td><\/tr>\n          <tr><td><code>ignore_early<\/code><\/td><td>no<\/td><td>Hang up after bridging and ignore early media.<\/td><\/tr>\n          <tr><td><code>src_cid_number<\/code><\/td><td>no<\/td><td>Caller ID displayed on the source leg.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <pre><code>curl -X POST \"https:\/\/api.kavkom.com\/api\/pbx\/v1\/active_call\/call\" \\\n  -H \"X-API-TOKEN: &lt;token&gt;\" \\\n  -H \"Content-Type: application\/json\" \\\n  -H \"Accept: application\/json\" \\\n  -d '{\n    \"domain_uuid\": \"ddcfeb62-110f-4bd7-b7a9-965e1a78295f\",\n    \"src\": \"901\",\n    \"destination\": \"33612345678\",\n    \"auto_answer\": \"true\"\n  }'<\/code><\/pre>\n        <pre><code>{\n  \"success\": true,\n  \"message\": \"Action completed successfully\",\n  \"call_uuid\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n  \"recording\": \"+OK\"\n}<\/code><\/pre>\n        <div class=\"kdoc-alert kdoc-alert--success\">Ideal for a <strong>Call<\/strong> button: the agent&#8217;s extension, <code>src<\/code>, rings first and is then connected to the customer.<\/div>\n\n        <h3>Hang up a call<\/h3>\n        <p><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>https:\/\/api.kavkom.com\/api\/pbx\/v1\/active_call\/kill<\/code><\/p>\n        <p>Expected body: <code>domain_uuid<\/code> and <code>call_uuid<\/code>, where <code>call_uuid<\/code> is returned by the origination endpoint.<\/p>\n      <\/section>\n\n      <section id=\"en-sip\">\n        <h2>Register an extension with SIP<\/h2>\n        <p>Once the extension has been ordered and provisioned, any SIP device or softphone can register on the PBX. The registration, or <code>REGISTER<\/code>, tells the switch where the user can be reached. It is required before making or receiving calls.<\/p>\n        <h3>Credentials<\/h3>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>SIP setting<\/th><th>Value<\/th><th>Source field<\/th><\/tr><\/thead><tbody>\n          <tr><td>Username \/ Auth user<\/td><td>The extension number, e.g. <code>901<\/code><\/td><td><code>extension<\/code><\/td><\/tr>\n          <tr><td>Password \/ Secret<\/td><td>The SIP secret<\/td><td><code>password<\/code><\/td><\/tr>\n          <tr><td>Domain \/ Realm \/ SIP server<\/td><td>The domain name, e.g. <code>client.kavkom.com<\/code><\/td><td><code>user_context<\/code><\/td><\/tr>\n          <tr><td>Transport<\/td><td><code>UDP<\/code>, <code>TCP<\/code> or <code>TLS<\/code>. Prefer <code>TLS<\/code>.<\/td><td>Device setting<\/td><\/tr>\n          <tr><td>Port<\/td><td><code>5060<\/code> UDP\/TCP or <code>5061<\/code> TLS<\/td><td>Device setting<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <div class=\"kdoc-alert\">The SIP realm must match the extension&#8217;s <code>user_context<\/code>. This is how the PBX determines the device domain.<\/div>\n        <h3>Registration flow<\/h3>\n        <ol class=\"kdoc-flow\">\n          <li>The device sends a <code>REGISTER<\/code> request to the registrar with <code>username@domain<\/code>.<\/li>\n          <li>The PBX replies with <code>401 Unauthorized<\/code> and a digest challenge.<\/li>\n          <li>The device sends another <code>REGISTER<\/code> with the computed digest.<\/li>\n          <li>On success, the switch returns <code>200 OK<\/code> and stores the contact.<\/li>\n        <\/ol>\n        <pre><code>SIP Username : 901\nPassword     : aB3xY9zK\nSIP Domain   : client.kavkom.com\nRegistrar    : client.kavkom.com\nTransport    : TLS\nPort         : 5061\nExpiry       : 3600<\/code><\/pre>\n        <div class=\"kdoc-alert kdoc-alert--warning\"><strong>Troubleshooting.<\/strong> A <code>403 Forbidden<\/code> on <code>REGISTER<\/code> generally means an incorrect password or a realm that differs from <code>user_context<\/code>. Behind NAT, enable keep-alive to remain reachable.<\/div>\n      <\/section>\n\n      <section id=\"en-webrtc\">\n        <h2>Build a WebRTC webphone application with SIP.js<\/h2>\n        <p>This section shows how to embed a phone directly in the browser using an extension&#8217;s credentials. The reference implementation uses SIP.js and its <code>SimpleUser<\/code> helper.<\/p>\n        <h3>Prerequisites<\/h3>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Item<\/th><th>Details<\/th><\/tr><\/thead><tbody>\n          <tr><td>API token<\/td><td>Authorized for your domain and for the extension list endpoint.<\/td><\/tr>\n          <tr><td><code>domain_uuid<\/code><\/td><td>UUID v4 of the customer domain.<\/td><\/tr>\n          <tr><td>Provisioned extension<\/td><td>An extension must already exist.<\/td><\/tr>\n          <tr><td>Modern browser<\/td><td>Microphone access is required; the page must be served over HTTPS.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <h3>Step 1 &#8211; Retrieve SIP credentials on the server side<\/h3>\n        <p>Call the extension list endpoint to read the three values required for registration: <code>extension<\/code>, <code>password<\/code> and <code>user_context<\/code>, which corresponds to the domain or realm. Check that the extension is <code>enabled: \"true\"<\/code>.<\/p>\n        <h3>Steps 2 &#038; 3 &#8211; Register and call from the browser<\/h3>\n        <pre><code>import { Web } from \"sip.js\";\n\n\/\/ 1) Retrieve credentials from YOUR backend.\n\/\/ Never call the Kavkom API with the token directly from the browser.\nconst { extension, password, user_context: domain } = await fetch(\n  \"\/api\/my-backend\/sip-credentials\"\n).then((r) =&gt; r.json());\n\n\/\/ 2) Build the SIP client from those values.\nconst sipTarget = (number) =&gt; `sip:${number}@${domain}`;\nconst simpleUser = new Web.SimpleUser(`wss:\/\/${domain}\/`, {\n  aor: sipTarget(extension),\n  media: { remote: { audio: document.querySelector(\"#remoteAudio\") } },\n  userAgentOptions: {\n    authorizationUsername: extension,\n    authorizationPassword: password,\n  },\n  delegate: {\n    onServerConnect: () =&gt; simpleUser.register(),\n    onCallReceived: () =&gt; simpleUser.answer(),\n  },\n});\n\n\/\/ 3) Connect + register, then place calls.\nawait simpleUser.connect(); \/\/ triggers onServerConnect -&gt; register()\n\/\/ await simpleUser.call(sipTarget(\"33612345678\"));\n\/\/ await simpleUser.hangup();<\/code><\/pre>\n        <p>During a call, the client can handle hold, mute, transfer and the DTMF keypad.<\/p>\n        <h3>Variant &#8211; Originate from the server<\/h3>\n        <p>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&#8217;s extension should ring first.<\/p>\n        <details><summary>The connection fails immediately<\/summary><p>The domain is wrong: it must match <code>user_context<\/code> and be reachable at <code>wss:\/\/&lt;domain&gt;\/<\/code>. The page must be served over HTTPS.<\/p><\/details>\n        <details><summary>The extension registers, then disconnects<\/summary><p>Check the password and make sure the realm matches <code>user_context<\/code>.<\/p><\/details>\n        <details><summary>There is no audio<\/summary><p>Microphone permission was probably denied, or no <code>&lt;audio&gt;<\/code> element is linked to the remote media.<\/p><\/details>\n        <details><summary>Calls cannot be placed<\/summary><p>Check that the extension is <code>enabled: \"true\"<\/code> and that the destination format is correct.<\/p><\/details>\n      <\/section>\n\n      <section id=\"en-websocket\">\n        <h2>Real-time call events &#8211; WebSocket<\/h2>\n        <p>The <code>\/calls<\/code> WebSocket pushes call status in real time for the requested extensions.<\/p>\n        <h3>Connection<\/h3>\n        <p><span class=\"kdoc-method kdoc-wss\">WSS<\/span> <code>wss:\/\/live.kavkom.com\/calls?t=&lt;access-token&gt;&amp;ext=1001<\/code><\/p>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Parameter<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>t<\/code><\/td><td>Access token obtained via <code>acquire-access<\/code> with the <code>calls<\/code> scope.<\/td><\/tr>\n          <tr><td><code>ext<\/code><\/td><td>Comma-separated list of extensions, for example <code>1001<\/code> or <code>1001,1002<\/code>.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <ul>\n          <li>Missing <code>t<\/code>: the server returns <code>406<\/code>, \u201c<code>&amp;t parameter is missing<\/code>\u201d.<\/li>\n          <li>Missing <code>ext<\/code>: <code>406<\/code>, \u201c<code>&amp;ext parameter is missing<\/code>\u201d.<\/li>\n          <li>Invalid or expired token: connection rejected with <code>401<\/code> or <code>Invalid token<\/code>.<\/li>\n        <\/ul>\n        <h3>Received messages<\/h3>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Event<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>call<\/code><\/td><td>Call state update: ringing, active, held, finished.<\/td><\/tr>\n          <tr><td><code>ping<\/code><\/td><td>Heartbeat; the client must reply with <code>pong<\/code>.<\/td><\/tr>\n          <tr><td><code>error<\/code><\/td><td>Error message in <code>data.message<\/code>.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <h3><code>call<\/code> event payload<\/h3>\n        <pre><code>{\n  \"event\": \"call\",\n  \"data\": {\n    \"callUuid\": \"abc-123\",\n    \"state\": \"active\",\n    \"direction\": \"inbound\",\n    \"number\": \"380501234567\",\n    \"presenceId\": \"1001@contact.example.kavkom.com\",\n    \"createdEpoch\": 1710763200,\n    \"answeredEpoch\": 1710763205,\n    \"duration\": 120\n  }\n}<\/code><\/pre>\n        <h3>Ping \/ Pong<\/h3>\n        <p>The server sends <code>ping<\/code> with <code>data: { sentStamp, expiresInSec }<\/code>. The client must return a <code>pong<\/code> with the same structure.<\/p>\n        <pre><code>{ \"event\": \"pong\", \"data\": { \"sentStamp\": 1710763200000, \"expiresInSec\": 60 } }<\/code><\/pre>\n        <h3>JavaScript client example<\/h3>\n        <pre><code>const ws = new WebSocket(\n  `wss:\/\/live.kavkom.com\/calls?t=${token}&amp;ext=1001`\n);\n\nws.onmessage = (msg) =&gt; {\n  const { event, data } = JSON.parse(msg.data);\n  if (event === \"call\") console.log(data.state, data.direction, data.number);\n  if (event === \"ping\") ws.send(JSON.stringify({ event: \"pong\", data }));\n};<\/code><\/pre>\n      <\/section>\n\n      <section id=\"en-webhooks\">\n        <h2>Webhooks &#8211; pushing calls &#038; CDRs<\/h2>\n        <p>From the Kavkom interface, under <strong>Advanced \u2192 Integrations \u2192 Webhook<\/strong>, 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.<\/p>\n        <h3>Configuration parameters<\/h3>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Field<\/th><th>Description<\/th><\/tr><\/thead><tbody>\n          <tr><td>Endpoint<\/td><td>HTTPS URL of your server. A test button lets you validate it.<\/td><\/tr>\n          <tr><td>Headers<\/td><td>Custom headers, for example <code>Authorization: Bearer \u2026<\/code>.<\/td><\/tr>\n          <tr><td>Variables<\/td><td>Custom variables added to the payload.<\/td><\/tr>\n          <tr><td>Call types<\/td><td>Live inbound calls, live outbound calls, and\/or CDRs after the call ends.<\/td><\/tr>\n          <tr><td>Mapping<\/td><td>Mapping between Kavkom fields and the fields expected by your system.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <h3>Example received payload<\/h3>\n        <pre><code>POST \/webhooks\/calls HTTP\/1.1\nAuthorization: Bearer &lt;your-key&gt;\nContent-Type: application\/json\n\n{\n  \"event\": \"cdr\",\n  \"callUuid\": \"a1b2c3d4-...-ef1234567890\",\n  \"direction\": \"outbound\",\n  \"number\": \"33612345678\",\n  \"state\": \"finished\",\n  \"duration\": 120\n}<\/code><\/pre>\n        <div class=\"kdoc-alert\"><strong>Native connectors.<\/strong> Without writing code, you can also connect HubSpot, Salesforce, Pipedrive, Zoho or Fireberry directly from the same integrations screen.<\/div>\n        <div class=\"kdoc-alert kdoc-alert--warning\"><strong>Good to know.<\/strong> Kavkom webhooks push events to your endpoint. Your server&#8217;s HTTP response does not control the call flow: call control is handled via the REST endpoints and the SIP client.<\/div>\n      <\/section>\n\n      <section id=\"en-reference\">\n        <h2>Endpoint reference<\/h2>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Method &#038; path<\/th><th>API<\/th><th>Role<\/th><\/tr><\/thead><tbody>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api.php<\/code> <small>(route=order, type=line)<\/small><\/td><td>Billing<\/td><td>Order a line \/ extension.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api.php<\/code> <small>(route=order, type=did)<\/small><\/td><td>Billing<\/td><td>Order a DID number.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-get\">GET<\/span> <code>\/api.php<\/code> <small>(route=order, type=didlist)<\/small><\/td><td>Billing<\/td><td>List DID orders.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-get\">GET<\/span> <code>\/api\/pbx\/v1\/extension\/list<\/code><\/td><td>PBX<\/td><td>List extensions.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-get\">GET<\/span> <code>\/api\/pbx\/v1\/extension\/list_limited<\/code><\/td><td>PBX<\/td><td>Lightweight extension list.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api\/pbx\/v1\/active_call\/call<\/code><\/td><td>PBX<\/td><td>Start a call.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api\/pbx\/v1\/active_call\/kill<\/code><\/td><td>PBX<\/td><td>Hang up a call.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api\/auth\/acquire-access<\/code><\/td><td>Auth<\/td><td>Get a WebSocket access token.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api\/auth\/verify-access<\/code><\/td><td>Auth<\/td><td>Verify or extend the token.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-post\">POST<\/span> <code>\/api\/auth\/revoke-access<\/code><\/td><td>Auth<\/td><td>Revoke the access token.<\/td><\/tr>\n          <tr><td><span class=\"kdoc-method kdoc-wss\">WSS<\/span> <code>\/calls?t=&amp;ext=<\/code><\/td><td>WebSocket<\/td><td>Call event stream.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n      <\/section>\n\n      <section id=\"en-errors\">\n        <h2>Error codes &#038; troubleshooting<\/h2>\n        <h3>Error format<\/h3>\n        <p>Billing API:<\/p>\n        <pre><code>{ \"status\": \"error\", \"message\": \"Fields required: domain_uuid, options\" }<\/code><\/pre>\n        <p>PBX API:<\/p>\n        <pre><code>{\n  \"error\": true,\n  \"message\": \"Model is not valid\",\n  \"validationErrors\": { \"extension\": \"The extension field is required.\" }\n}<\/code><\/pre>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Case<\/th><th>Meaning \/ action<\/th><\/tr><\/thead><tbody>\n          <tr><td><code>422<\/code> + \u201csrc invalid\u201d<\/td><td>The source extension does not exist on this domain.<\/td><\/tr>\n          <tr><td><code>422<\/code> + switch text <code>-ERR \u2026<\/code><\/td><td>The call was rejected by the switch.<\/td><\/tr>\n          <tr><td>Rejected token<\/td><td>The token is not authorized for the route, or is not linked to the supplied <code>domain_uuid<\/code>.<\/td><\/tr>\n          <tr><td><code>406<\/code> WebSocket<\/td><td>Missing <code>t<\/code> or <code>ext<\/code> parameter.<\/td><\/tr>\n          <tr><td><code>401<\/code> WebSocket<\/td><td>Invalid or expired token.<\/td><\/tr>\n          <tr><td><code>403<\/code> SIP REGISTER<\/td><td>Incorrect password or realm different from <code>user_context<\/code>.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n      <\/section>\n\n      <section id=\"en-glossary\">\n        <h2>Glossary<\/h2>\n        <div class=\"kdoc-table-wrap\"><table><thead><tr><th>Term<\/th><th>Definition<\/th><\/tr><\/thead><tbody>\n          <tr><td>DID<\/td><td>Direct Inward Dialing: a phone number that can be reached directly.<\/td><\/tr>\n          <tr><td>Extension \/ line<\/td><td>Telephone endpoint on the PBX, identified by a short number such as <code>901<\/code>.<\/td><\/tr>\n          <tr><td><code>domain_uuid<\/code><\/td><td>UUID v4 identifying the customer, or tenant, on the platform.<\/td><\/tr>\n          <tr><td>CDR<\/td><td>Call Detail Record: metadata for a completed call.<\/td><\/tr>\n          <tr><td>IVR<\/td><td>Interactive voice response, or touch-tone menu.<\/td><\/tr>\n          <tr><td>SIP<\/td><td>Session initiation protocol for voice over IP.<\/td><\/tr>\n          <tr><td>WebRTC<\/td><td>Real-time communication technology in the browser.<\/td><\/tr>\n          <tr><td>Bridge<\/td><td>Connection of two call legs.<\/td><\/tr>\n        <\/tbody><\/table><\/div>\n        <p class=\"kdoc-footer\">Kavkom API Documentation &#8211; Version 1.0, June 2026.<\/p>\n      <\/section>\n    <\/main>\n  <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Phone numbers, calls, click-to-call, WebRTC webphones, recording, and real-time events \u2014 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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[52,84],"tags":[],"class_list":["post-4004","post","type-post","status-publish","format-standard","hentry","category-api-integration","category-kavkom-api"],"_links":{"self":[{"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/posts\/4004","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/comments?post=4004"}],"version-history":[{"count":7,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/posts\/4004\/revisions"}],"predecessor-version":[{"id":9185,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/posts\/4004\/revisions\/9185"}],"wp:attachment":[{"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/media?parent=4004"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/categories?post=4004"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/help.kavkom.com\/en\/wp-json\/wp\/v2\/tags?post=4004"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}