XURA Overview

Integration guide · API, webhooks and phone line

Xura Global AI Voice Agents

How to connect Xura Global AI Voice Agents to your organization: your CRM, your phone line and, if you want, WhatsApp. Written for the technical team that carries out the integration.

Version1.0 Last updated25 September 2026 APIv1

Overview

Xura Global AI Voice Agents make and answer phone calls for your organization in natural spoken Sinhala. Each agent knows who it is talking to from your CRM, follows the call profile agreed with your team, answers questions from your own documents, and sends the result of every call back to your systems.

Every organization connects in the same way: three integration points link your systems with the agents, and calls travel over your own phone line.

Integration map Your systems connect to Xura Global AI Voice Agents through the Contact API, the webhook receiver and the Voice API. The agents reach contacts through your telecom operator's SIP trunk and, optionally, through WhatsApp. Your organization Front end for your staff CRM and core systems ① Contact API ② Webhook receiver Xura Global AI Voice Agents ③ Voice API AI voice agents (Sinhala) Call profiles Knowledge base Campaigns Telephony gateway Telecom operator ④ SIP trunk Your phone numbers → contacts' phones WhatsApp Optional, through Meta SIP over TLS → WhatsApp users ③ start calls ① fetch contact ② call results calls calls
The four connections. ①, ② and your front end are built by your team; ③ is provided by Xura Global; ④ is ordered by your team from a licensed operator and connected by Xura Global.
#ConnectionDirectionSet up byUsed for
1Contact APIXura Global → your systemsYour teamKnowing who the agent is talking to, and the facts for the call
2Webhook receiverXura Global → your systemsYour teamReceiving the result of every call: outcome, captured data, summary and transcript
3Voice APIYour systems → Xura GlobalXura GlobalStarting calls and campaigns, managing knowledge documents, reading results
4Phone line (SIP trunk)Both waysYour team, your operator and Xura GlobalCarrying calls to and from your phone numbers
+WhatsApp callingBoth waysYour team and Xura GlobalOptional: calls with your customers on WhatsApp

What organizations use it for

Payment remindersRemind customers of overdue or upcoming payments and agree a payment date.
Appointment remindersConfirm, reschedule or cancel appointments before the day.
Order and delivery confirmationConfirm orders, delivery dates and cash-on-delivery amounts.
Lead follow-upCall people who enquired, answer their questions and find out who is interested.
Customer service lineAnswer calls to your hotline day and night from your knowledge base.
Feedback surveysAsk a few questions after a purchase or visit and record the answers.

The integration is the same for all of them. Only the call profile is specific to your organization: what the agent says, what it needs to know and what it records. Each organization also gets its own dedicated system and pays AI and phone usage directly to Google and its telecom operator (see your system and costs).

Integration process

Every project follows the same five phases. Steps marked Mandatory are needed before go-live; the others depend on how you use the agents.

  1. 1

    Plan

    Agree the call profilesBoth teamsMandatory

    Decide what each kind of call should achieve, what the agent may say, which contact fields it needs and which outcomes you want back. Call profiles

    Order the phone lineYour teamYour operatorMandatory

    Order a SIP trunk and phone numbers from a licensed telecom operator and choose how it connects (option A, B or C). Start early: operator provisioning is often the longest step. Phone line

    Set up your accountsYour teamMandatory

    Create a Google Cloud project with billing (for Gemini and Speech-to-Text) and decide where your system runs: on a server in your name or hosted by Xura Global. Your system and costs

    Exchange addresses and keysBoth teamsMandatory

    Base URLs, API keys, the webhook signing secret and firewall addresses, for testing and for production. Onboarding checklist

  2. 2

    Build

    Build the Contact API ①Your teamMandatory

    Two read-only endpoints that return a contact and the facts for the call. Needed for inbound calls and campaigns. Contact API

    Build the webhook receiver ②Your teamMandatory

    One HTTPS endpoint that verifies and stores the result of every call. Webhook receiver

    Connect your systems to the Voice API ③Your teamFor outbound calls

    Start calls and campaigns from your systems and show results in your own front end. Not needed if the agents only answer calls. Voice API

    Prepare knowledge documentsYour teamRecommended

    Products, services, procedures, fees, policies and frequent questions the agents may answer from. Knowledge base

    Install and configure your systemXura GlobalMandatory

    Xura Global installs your dedicated system, connects it to your Google Cloud project, sets up your call profiles, voice and calling hours, and loads your knowledge documents.

  3. 3

    Connect

    Connect the phone line ④Your teamYour operatorXura GlobalMandatory

    Connect the SIP trunk to the Xura Global gateway and make test calls in both directions. Phone line

    Connect WhatsApp callingYour teamXura GlobalOptional

    Let customers call you on WhatsApp, and call them there once they allow it. WhatsApp calling

  4. 4

    Test

    Run the acceptance testsBoth teamsMandatory

    Tests of data, results, calls, knowledge and campaigns, run together in the testing environment. Testing and go-live

  5. 5

    Go live

    PilotBoth teamsMandatory

    Start with a small group of contacts. Review transcripts and outcomes together and adjust the profiles.

    Roll out and monitorBoth teamsMandatory

    Switch to production keys and numbers. Failed webhooks, Contact API errors and phone line faults alert both teams.

Who does what

AreaXura GlobalYour team
AI voice agent software and call handlingBuilds and maintains it—
Your dedicated system (server with the agents, gateway and database)Installs, updates and monitors itProvides a server in your name, or uses hosting by Xura Global
AI services (Google Cloud: Gemini and Speech-to-Text)Connects them to your systemOwns the Google Cloud project and pays for usage
Call profilesConfigures and tests themDefines the goals and rules, and approves them
Contact data (the source of truth)Reads it through ①Owns it and builds ① Contact API
Call resultsSends them through ②Builds ② Webhook receiver, stores the results and acts on them
Front end for your staff (call lists, results, reports)Provides ③ Voice APIBuilds the front end on ③
Phone numbers and SIP trunk ④Connects the trunk to the gatewayOrders them from a licensed operator and holds the contract
WhatsApp calling (optional)Connects WhatsApp calls to the agentsHolds the WhatsApp Business account and number
Knowledge base contentHosts and indexes itWrites the documents and keeps them up to date
Follow-up actions (call-backs, reminders, opt-outs)Flags them in each resultCarries them out
Note

The Xura Global dashboard used in demos is an internal tool for testing and support. Your staff use your own front end, built on the Voice API.

Your system and running costs

Every organization gets its own dedicated system: its own server (or its own isolated Docker installation), database, telephony gateway and API keys. Nothing is shared with other organizations, and usage is billed directly to your own accounts.

CostPaid byPaid to
AI usage: Gemini and Speech-to-TextYour organizationGoogle Cloud, through your own project
Phone numbers and calls (SIP trunk)Your organizationYour telecom operator
WhatsApp calls (optional)Your organizationMeta, through your WhatsApp Business account
ServerYour organizationYour hosting provider, or Xura Global if it hosts your system
Setup, and the optional monthly maintenance planYour organizationXura Global
  • Maintenance plan (optional). Software updates, security patches, monitoring, backups and support. Without a plan, Xura Global helps on request.
  • Budget alert. Set a monthly budget alert in your Google Cloud project so AI costs never surprise you.
  • Keep billing enabled. Under Google's terms for paid use, the Gemini API does not use your call data to improve Google's products, and Speech-to-Text does so only if you opt in to data logging.

Call profiles

A call profile describes one kind of call: its purpose, what the agent says and checks, the contact data it needs and the outcomes it records. Your team and Xura Global agree each profile at the start of the project; Xura Global configures it and you approve it with test calls. An organization can have several profiles, for example a payment reminder for outbound calls and a customer service line for inbound calls.

What a profile defines

  1. PurposeThe goal of the call, in one sentence.
  2. DirectionOutbound, inbound or both, and on which numbers.
  3. GreetingHow the agent introduces itself and your organization.
  4. Identity checkWhat the agent confirms before it discusses any detail.
  5. Context fieldsThe facts the agent needs from your Contact API.
  6. Outcomes and captured dataThe result codes and values you want back.
  7. Rules and limitsWhat the agent may agree to, and what it must never say.
  8. HandoverWhen the agent promises a call back from your staff.
  9. Calling hours and retriesHours, days, holidays, attempts and the gap between attempts.
  10. Voice and knowledgeThe agent's voice and the documents it may answer from.

Example profiles

Starting points that are adapted to each organization. The meaning of every code is in Reference codes.

ProfileTypical organizationsContext fieldsOutcome codes
Payment reminder
payment-reminder
Banks, finance and leasing companies, telcos, utilities, insurers, schoolsamountDue, currency, dueDatePROMISE_TO_PAY, ALREADY_PAID, FINANCIAL_HARDSHIP, REFUSED_TO_PAY, DISPUTED
Appointment reminder
appointment-reminder
Hospitals, clinics, laboratories, service centresappointmentAt, withWhom, locationCONFIRMED, RESCHEDULE_REQUESTED, CANCELLED
Order and delivery confirmation
delivery-confirmation
Online shops, couriers, distributorsorderNo, deliveryDate, codAmountCONFIRMED, CHANGE_REQUESTED, CANCELLED
Lead follow-up
lead-follow-up
Real estate, vehicle sales, education, insuranceproduct, enquiryDateINTERESTED, NOT_INTERESTED
Customer service line
customer-service
Any organization with a hotline (inbound)Optional account summaryRESOLVED
Feedback survey
feedback-survey
Any organizationserviceDate, serviceTypeSURVEY_COMPLETED, DECLINED

Every profile also uses the standard outcome codes, such as CALLBACK_REQUESTED, OPT_OUT and NO_ANSWER.

How the agents behave on every call

These rules are built into every agent and apply to all profiles. Your profile can add to them but not remove them.

  • The agent introduces itself with your organization's name, and says it is an automated assistant when asked.
  • It confirms it is speaking to the right person before discussing any personal detail, and never discusses a contact's details with anyone else.
  • It answers only from your contact data and your knowledge base, and never invents figures, dates or policies. What it cannot answer becomes a call-back request.
  • It stays polite and never threatens, pressures or argues.
  • It reads important details (dates, times, amounts) back and waits for the contact to confirm them.
  • When a contact asks not to be called again, it ends the call politely and reports OPT_OUT.
  • It never calls contacts marked doNotCall, and campaigns call only within your calling hours. A call ends after 10 minutes at most.

Changing a profile. Tell Xura Global what should change. Wording and rules change after a test call; new context fields or outcome codes are agreed with your team first because they change the data you send or receive.

How a call flows

Outbound call

Your system asks the agents to call one contact. The agent fetches fresh data from your Contact API before it dials, so it never uses old figures.

sequenceDiagram
    autonumber
    participant Y as Your system
    participant X as Xura Voice API
    participant C as ① Contact API
    participant P as Contact's phone
    participant W as ② Webhook
    Y->>X: POST /v1/calls
    X->>C: GET /contacts/{id}
    C-->>X: 200 contact
    X-->>Y: 202 queued
    X->>P: Rings the phone (④)
    P-->>X: Answers
    X->>W: call.started
    Note over X,P: Conversation in Sinhala
    X->>W: call.completed
    W-->>X: 200 OK
          

Inbound call

A contact calls one of your numbers. The agent looks the caller up by phone number while it greets them.

sequenceDiagram
    autonumber
    participant P as Contact's phone
    participant X as Xura AI Voice Agent
    participant C as ① Contact API
    participant W as ② Webhook
    P->>X: Calls your number (④ or WhatsApp)
    X->>C: GET /contacts/lookup?phone=…
    C-->>X: 200 contact, or 404 unknown
    X->>W: call.started
    Note over P,X: Agent confirms the name, then helps
    X->>W: call.completed
          

Campaign

A campaign calls a list of contacts automatically, one after another, only within the calling hours you set. Unanswered calls are retried.

sequenceDiagram
    participant Y as Your system
    participant X as Xura Voice API
    participant W as ② Webhook
    Y->>X: POST /v1/campaigns
    X-->>Y: 201 running
    loop Each contact, within calling hours
        X->>X: Fetch contact (①) and dial
        X->>W: call.completed or call.not_connected
    end
    Note over X: No answer or busy:<br/>retried after retryMinutes,<br/>up to maxAttempts
    X->>W: campaign.completed
          

Conventions

These rules apply to ①, ② and ③.

Transport
HTTPS only, TLS 1.2 or later, with a certificate from a public authority. Plain HTTP is refused.
Format
JSON, UTF-8 (Content-Type: application/json; charset=utf-8). Sinhala text is sent as normal UTF-8 characters.
Phone numbers
E.164 with the country code: +94771234567. In a query string the + is URL-encoded as %2B.
Timestamps
ISO 8601. Xura Global sends UTC, for example 2026-09-25T04:44:50Z (10:14:50 in Sri Lanka). You may send any offset, for example 2026-10-02T09:30:00+05:30.
Calendar dates
YYYY-MM-DD in your organization's time zone (Asia/Colombo unless agreed otherwise), for example a due date or a promised date.
Money
A number with up to two decimals plus an ISO 4217 currency code, for example 5000 and "LKR". No thousands separators.
IDs
Strings. Your contact IDs are passed through unchanged (up to 64 characters). Call IDs from Xura Global are UUIDs.
Profile IDs
Lowercase words joined by hyphens, agreed at onboarding, for example payment-reminder.
Versions
Version 1 of this contract. Within v1 we only add optional fields and new event types, so ignore fields and events you do not recognise. Breaking changes get a new version and advance notice.

Built by your team1Contact API

A small read-only API in front of your CRM. The agents call it to find out who they are talking to and what the call is about. It has two endpoints and returns one object, the contact.

Base URL
Your choice, for example https://api.your-company.com/xura/v1. Send us one for testing and one for production.
Authentication
An API key that you issue to Xura Global, sent as Authorization: Bearer <key>. Please rotate it at least every 90 days.
Network
Allow requests from your system's IP address, given at onboarding. You may block everything else.
Speed
Respond within 800 ms for 95% of requests. We stop waiting after 2 seconds (see errors and time limits).
Availability
Contacts can call your numbers at any time, so the lookup endpoint should be available around the clock.
GET/contacts/lookup?phone={e164}YOUR TEAM

Find a contact by phone number. We call this for every inbound call, on the phone line and on WhatsApp, as soon as the call connects.

Query parameterTypeDescription
phonestringThe caller's number in E.164, URL-encoded: %2B94771234567.
  • 200 with the contact object when the number belongs to a contact.
  • 404 when it does not. The agent treats the caller as unknown: it takes their name and reason and records a call-back request.
  • If several contacts share the number (a family phone, for example), return the one your staff would want the agent to talk about first, such as the one with the most urgent open item. The agent confirms the name before discussing anything.
Request
GET /xura/v1/contacts/lookup?phone=%2B94771234567 HTTP/1.1
Host: api.your-company.com
Authorization: Bearer ck_live_4f7c9b1e…
Accept: application/json
Response · 404
{ "error": { "code": "not_found", "message": "No contact uses this phone number." } }
GET/contacts/{contactId}YOUR TEAM

Get a contact by your ID. We call this when a call is requested through the Voice API, and before every campaign attempt, so the agent uses today's data.

Response · 200
{
  "contactId": "C-10042",
  "name": "Nimal Perera",
  "spokenName": "නිමල්",
  "salutation": "මහත්තයා",
  "phone": "+94771234567",
  "language": "si",
  "doNotCall": false,
  "context": {
    "amountDue": 5000,
    "currency": "LKR",
    "dueDate": "2026-08-10"
  }
}

Contact object

Send only these fields. The agents do not need, and should not receive, national ID numbers, addresses or full account histories.

FieldTypeRequiredDescription
contactIdstringYesYour ID for the person the agent talks to. It comes back in every event about this contact.
namestringYesFull name as in your records.
spokenNamestringRecommendedThe name in Sinhala script, the way the agent should say it (නිමල්). Without it the agent reads name.
salutationstringRecommendedHow the agent addresses the contact: මහත්තයා, මහත්මිය, මෙනවිය, or empty.
phonestringYesThe number to call, in E.164.
languagestringNoLanguage of the call. si (Sinhala) is the default; other languages are enabled per project.
doNotCallbooleanNotrue: the agents never call this contact. Their own calls are still answered. Default false.
contextobjectPer profileThe facts the agent may use on this call. Field names and types are agreed in the call profile. Up to 30 fields and 4 KB.

Context examples

context
{ "amountDue": 5000, "currency": "LKR", "dueDate": "2026-08-10" }
  • Use the formats in Conventions: numbers for amounts, YYYY-MM-DD for dates, ISO 8601 for times.
  • Include only what the agent needs to say or check. It uses context only after the identity check.
  • Never include passwords, PINs, full card or account numbers, national ID numbers or detailed medical information.

Errors and time limits

Your responseInbound callOutbound call
200 within 2 sThe agent uses the dataThe call is placed
200 with doNotCall: trueAnswered as normalNot placed. Voice API: 422 contact_not_callable. Campaign: contact skipped (CANCELED)
404Caller treated as unknownNot placed. Voice API: 404 contact_not_found. Campaign: contact skipped (FAILED)
401 / 403Caller treated as unknown; Xura Global is alertedNot placed. Voice API: 502 contact_api_error. Campaign: tried again at the next attempt
429 / 5xx or no answer in 2 sWe retry once after 300 ms, then treat the caller as unknownWe retry once, then as for 401 / 403

Built by your team2Webhook receiver

One HTTPS endpoint on your side. We send it an event every time something happens to a call or a campaign. Your endpoint must:

  • Accept POST requests with a JSON body at a public HTTPS URL, for example https://api.your-company.com/webhooks/xura.
  • Check the signature on every request and reject requests that fail it.
  • Answer with a 2xx status within 10 seconds. Save the event first and do the slow work (CRM updates, SMS) afterwards.
  • Process each event once. The same event can arrive more than once, so skip any id you have already stored.
  • Not rely on arrival order. Use occurredAt to order events for the same call.

Event types

EventSent whenMain content
call.startedA contact answers our call, or the agent answers theirsCall
call.completedAn answered call has ended and been analysed (usually 2–5 s after hang-up)Call, outcome, transcript
call.not_connectedAn outbound call was not answered, was busy, failed, was cancelled or could not be placedCall, outcome, campaign retry plan
campaign.completedEvery contact in a campaign has a final result, or the campaign was stoppedCampaign totals
webhook.testA test is requested with POST /v1/webhook/testA test message

Envelope

Every event has the same outer shape:

FieldTypeDescription
idstringUnique event ID, for example evt_5f0c2b7e9d8a4c21. Use it to skip duplicates.
typestringOne of the event types above.
occurredAttimestampWhen it happened.
apiVersionstringv1.
dataobjectThe content. See payload examples and call and outcome fields.

Headers we send

HeaderExample
Content-Typeapplication/json; charset=utf-8
User-AgentXuraGlobal-Webhooks/1.0
X-Xura-Event-Idevt_5f0c2b7e9d8a4c21
X-Xura-Event-Typecall.completed
X-Xura-Signaturet=1790311541,v1=6b1d8f…c02a

Verifying signatures

We sign every request with a secret that only you and Xura Global know (the webhook signing secret, starting with whsec_, shared at onboarding). Checking it proves that the request came from Xura Global and was not changed on the way.

  1. Read t (Unix time in seconds) and v1 (the signature) from the X-Xura-Signature header.
  2. Compute HMAC-SHA256 of t + "." + raw request body with the secret, as lowercase hex. Use the raw bytes exactly as received, before any JSON parsing.
  3. Compare it with v1 using a constant-time comparison.
  4. Reject the request with 401 if the signatures differ or if t is more than 5 minutes away from your clock.
Express
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const SECRET = process.env.XURA_WEBHOOK_SECRET; // whsec_…

app.post('/webhooks/xura', express.raw({ type: 'application/json' }), (req, res) => {
  const parts = Object.fromEntries(
    (req.get('X-Xura-Signature') ?? '').split(',').map((p) => p.split('=')),
  );
  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${parts.t}.`)
    .update(req.body) // raw bytes
    .digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
  const valid =
    parts.v1?.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  if (!fresh || !valid) return res.sendStatus(401);

  const event = JSON.parse(req.body);
  saveEvent(event); // store it, then process asynchronously (skip if event.id is already stored)
  res.sendStatus(200);
});

Retries

If your endpoint does not answer with a 2xx within 10 seconds, we send the same event again (same id):

Attempt123456
Sent afterat once1 min5 min30 min2 h6 h

After the sixth attempt the event is marked as failed and both teams are alerted. The result is not lost: you can always read it with GET /v1/calls/{callId}, and we can send failed events again once your endpoint is back.

Payload examples

call.completed

The outcome.data values depend on the call profile. Two examples:

POST https://api.your-company.com/webhooks/xura
{
  "id": "evt_5f0c2b7e9d8a4c21",
  "type": "call.completed",
  "occurredAt": "2026-09-25T04:45:41Z",
  "apiVersion": "v1",
  "data": {
    "call": {
      "id": "3ec2bcda-8289-4a84-90bc-46fc2f80f77a",
      "direction": "outbound",
      "channel": "phone",
      "profile": "payment-reminder",
      "status": "completed",
      "contactId": "C-10042",
      "phone": "+94771234567",
      "externalRef": "INV-2026-08-00123",
      "metadata": { "branch": "Kandy" },
      "campaign": null,
      "startedAt": "2026-09-25T04:44:50Z",
      "endedAt": "2026-09-25T04:45:38Z",
      "durationSec": 48,
      "endReason": "agent_completed"
    },
    "outcome": {
      "code": "PROMISE_TO_PAY",
      "summary": "Nimal promised to pay the overdue LKR 5,000 on 2 October 2026. The agent read the date back and he confirmed it.",
      "sentiment": "POSITIVE",
      "data": { "promisedDate": "2026-10-02", "promisedAmount": 5000, "currency": "LKR" },
      "followUp": null
    },
    "transcript": [
      { "role": "agent", "text": "ආයුබෝවන්! මම ඒබීසී සමාගමෙන් කතා කරන්නේ. මට නිමල් මහත්තයා එක්ක කතා කරන්න පුළුවන්ද?", "at": "2026-09-25T04:44:51Z" },
      { "role": "contact", "text": "ඔව්, නිමල් කතා කරන්නේ.", "at": "2026-09-25T04:44:58Z" },
      { "role": "agent", "text": "නිමල් මහත්තයා, අගෝස්තු දහවෙනිදා ගෙවන්න තිබුණ රුපියල් පන්දාහක මුදලක් තවම ගෙවලා නැහැ. ඒක කවදා ගෙවන්න පුළුවන්ද?", "at": "2026-09-25T04:45:00Z" },
      { "role": "contact", "text": "මම ලබන සිකුරාදා ගෙවන්නම්.", "at": "2026-09-25T04:45:21Z" },
      { "role": "agent", "text": "එතකොට ඔක්තෝබර් දෙවෙනිදා රුපියල් පන්දාහ, හරිද?", "at": "2026-09-25T04:45:23Z" },
      { "role": "contact", "text": "ඔව්, හරි.", "at": "2026-09-25T04:45:31Z" },
      { "role": "agent", "text": "ස්තූතියි නිමල් මහත්තයා. සුභ දවසක්!", "at": "2026-09-25T04:45:33Z" }
    ]
  }
}

call.not_connected (a campaign attempt that will be retried)

body
{
  "id": "evt_8a41d3c09b7e2f65",
  "type": "call.not_connected",
  "occurredAt": "2026-09-25T05:02:41Z",
  "apiVersion": "v1",
  "data": {
    "call": {
      "id": "b7f9e2c1-4d3a-4c8e-9f61-2a5d7e0c9b13",
      "direction": "outbound",
      "channel": "phone",
      "profile": "appointment-reminder",
      "status": "not_connected",
      "contactId": "P-2307",
      "phone": "+94763456789",
      "externalRef": "APT-77134",
      "metadata": null,
      "campaign": { "id": "CAMP-6D2E91AF", "attempt": 1, "willRetry": true, "nextAttemptAt": "2026-09-25T06:32:41Z" },
      "startedAt": "2026-09-25T05:02:10Z",
      "endedAt": "2026-09-25T05:02:41Z",
      "durationSec": 0,
      "endReason": "no_answer"
    },
    "outcome": { "code": "NO_ANSWER", "summary": "The call was not answered.", "sentiment": null, "data": null, "followUp": null }
  }
}

call.started

body
{
  "id": "evt_1c7e4b92a0d35f8e",
  "type": "call.started",
  "occurredAt": "2026-09-25T05:20:03Z",
  "apiVersion": "v1",
  "data": {
    "call": {
      "id": "e41f7a2b-9c6d-4e1f-8b3a-5d2c9f0e7a64",
      "direction": "inbound",
      "channel": "phone",
      "profile": "customer-service",
      "status": "in_progress",
      "contactId": "C-10042",
      "phone": "+94771234567",
      "startedAt": "2026-09-25T05:20:03Z"
    }
  }
}

campaign.completed

body
{
  "id": "evt_d93b0e5f7c2a4186",
  "type": "campaign.completed",
  "occurredAt": "2026-09-25T11:30:05Z",
  "apiVersion": "v1",
  "data": {
    "campaign": {
      "id": "CAMP-6D2E91AF",
      "name": "October appointment reminders",
      "profile": "appointment-reminder",
      "channel": "phone",
      "status": "completed",
      "startedAt": "2026-09-25T03:30:00Z",
      "finishedAt": "2026-09-25T11:30:05Z",
      "totals": { "contacts": 120, "reached": 94, "notReached": 21, "skipped": 5 },
      "byOutcome": {
        "CONFIRMED": 68,
        "RESCHEDULE_REQUESTED": 13,
        "CANCELLED": 4,
        "CALLBACK_REQUESTED": 6,
        "WRONG_PERSON": 3,
        "NO_ANSWER": 17,
        "BUSY": 4
      }
    }
  }
}

Call and outcome fields

Call

FieldTypeDescription
idstringCall ID (UUID).
directionstringoutbound (the agent called) or inbound (the contact called).
channelstringphone (your SIP trunk) or whatsapp.
profilestringThe call profile used, for example payment-reminder.
statusstringqueued, dialing, in_progress, completed or not_connected. See call status.
contactIdstring or nullYour contact ID. null for an unknown inbound caller.
phonestringThe contact's number (E.164).
externalRefstring or nullYour own reference, if you sent one when starting the call.
metadataobject or nullYour key–value data from the call request, returned unchanged.
campaignobject or nullFor campaign calls: id, attempt, willRetry, nextAttemptAt.
startedAttimestampWhen the call was answered or, for a call that was not connected, when dialling began.
endedAttimestampWhen the call ended.
durationSecintegerTalk time in seconds. 0 when not connected.
endReasonstringWhy the call ended. See end reasons.

Outcome

FieldTypeDescription
codestringThe result of the call: a standard code or a code of the call profile.
summarystringOne or two sentences in English for your staff.
sentimentstring or nullPOSITIVE, NEUTRAL, NEGATIVE or HOSTILE. null when not connected.
dataobject or nullValues captured on the call, as defined in the call profile, for example promisedDate and promisedAmount. Every value was read back to the contact and confirmed.
followUpobject or nullSet when your staff should act: reason and, if the contact gave one, preferredTime in their own words.

Transcript

A list of { role, text, at } entries in call order. role is agent, contact or kb (a knowledge-base lookup: text is the question looked up and sources lists the documents used). The contact's words are transcribed automatically and can contain recognition errors; the outcome fields are the reliable record of what was agreed.

Provided by Xura Global3Voice API

Your back end calls this API to start calls and campaigns, read results and manage the knowledge base. Call it from your servers only: the API key must never be placed in a web page or a mobile app.

Basics

Base URL
Your dedicated system's own address, given at onboarding, for example https://voice.your-company.com/v1. Test and live keys use the same address. Examples below use https://voice-api.example.com/v1.
Authentication
Authorization: Bearer xv_live_… (production) or xv_test_… (testing). The keys are created on your system and work only there. We send them securely at onboarding and rotate them on request.
Idempotency
Send a unique Idempotency-Key header (a UUID) with every POST that creates something. Repeating a request with the same key within 24 hours returns the first response and never places a second call.
Rate limit
60 requests per minute per key. Above that you get 429 with a Retry-After header in seconds.
Concurrent calls
Limited by the channels on your phone line and the size of your server. A campaign places one call at a time.
Profiles
Your call profiles are configured by Xura Global. GET /v1/profiles lists them with their context fields and outcome codes.
Lists
?limit= (1–100, default 50) and ?cursor=. Responses are { "data": [...], "nextCursor": "…" }; nextCursor is null on the last page.
MethodPathPurpose
POST/v1/callsStart an outbound call
GET/v1/calls/{callId}Get a call with its outcome and transcript
GET/v1/callsList calls
POST/v1/calls/{callId}/cancelStop a ringing call or end a live call
POST/v1/campaignsCreate a campaign
GET/v1/campaignsList campaigns
GET/v1/campaigns/{campaignId}Progress and per-contact results
POST/v1/campaigns/{campaignId}/start · /pause · /stopControl a campaign
GET/v1/profilesList your call profiles
GET/v1/knowledge/documentsList knowledge documents
POST/v1/knowledge/documentsAdd a document
PUT/v1/knowledge/documents/{documentId}Edit a document, or switch it on or off
DELETE/v1/knowledge/documents/{documentId}Delete a document
POST/v1/knowledge/askPreview the agent's answer to a question
GET PUT/v1/webhookRead or set your webhook URL
POST/v1/webhook/testSend a webhook.test event
GET PUT/v1/settingsAgent voice and default calling hours
GET/v1/eventsLive call updates (optional)
GET/v1/healthService status (no key needed)

Calls

POST/v1/callsXURA GLOBAL

Start an outbound call. We fetch the contact from your Contact API, check it can be called, and dial. The result arrives on your webhook.

FieldTypeRequiredDescription
contactIdstringYes*Your contact ID.
contactobjectNo*The full contact object, instead of contactId. We then do not call your Contact API for this call.
profilestringIf severalThe call profile, for example payment-reminder. Required when you have more than one outbound profile.
channelstringNophone (default) or whatsapp (see WhatsApp calling).
phonestringNoCall this number instead of the contact's (E.164).
externalRefstringNoYour own reference (up to 64 characters), returned in every event about this call.
metadataobjectNoUp to 20 string key–value pairs (1 KB), returned unchanged.

* Send contactId or contact, not both.

Request
curl https://voice-api.example.com/v1/calls \
  -H "Authorization: Bearer xv_test_9b2e…" \
  -H "Idempotency-Key: 6c1d2c1e-0f7a-4d52-9c1b-2f8e7a3d4b10" \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "C-10042", "profile": "payment-reminder", "externalRef": "INV-2026-08-00123", "metadata": { "branch": "Kandy" } }'
Response · 202
{
  "id": "3ec2bcda-8289-4a84-90bc-46fc2f80f77a",
  "direction": "outbound",
  "channel": "phone",
  "profile": "payment-reminder",
  "status": "queued",
  "contactId": "C-10042",
  "phone": "+94771234567",
  "externalRef": "INV-2026-08-00123",
  "createdAt": "2026-09-25T04:44:21Z"
}
Request body with the contact included
{
  "profile": "appointment-reminder",
  "contact": {
    "contactId": "P-2291",
    "name": "Sunitha Fernando",
    "spokenName": "සුනීතා",
    "salutation": "මහත්මිය",
    "phone": "+94712345678",
    "context": { "appointmentAt": "2026-10-02T09:30:00+05:30", "withWhom": "Dr. Perera", "location": "Kandy branch" }
  },
  "externalRef": "APT-77120"
}

Refused with 422 contact_not_callable when the contact has doNotCall: true, and with 503 line_unavailable when the phone line is down.

GET/v1/calls/{callId}XURA GLOBAL

Get a call with the same call, outcome and transcript fields as the call.completed event. While a call is in progress, outcome is null and the transcript grows.

GET/v1/callsXURA GLOBAL

List calls, newest first, without transcripts.

Query parameterDescription
statusin_progress, completed, not_connected
direction, channelinbound or outbound; phone or whatsapp
profile, outcomeA profile ID; an outcome code, for example PROMISE_TO_PAY
contactId, campaignIdOnly calls for this contact or campaign
from, toTime range (timestamps)
limit, cursorPaging, see basics
POST/v1/calls/{callId}/cancelXURA GLOBAL

Stop a call. A ringing call is cancelled (call.not_connected, CANCELED). A live call is ended politely and analysed as usual (call.completed, end reason ended_by_operator).

Campaigns

POST/v1/campaignsXURA GLOBAL

Create a campaign that calls a list of contacts automatically. Each contact is fetched from your Contact API just before its call.

FieldTypeRequiredDescription
namestringYesA name your staff recognise (up to 100 characters).
profilestringIf severalThe call profile. Required when you have more than one outbound profile.
channelstringNophone (default) or whatsapp.
contactIdsstring[]Yes1 to 1,000 of your contact IDs, called in this order.
maxAttemptsintegerNoCalls per contact if unanswered, busy or failed: 1–5, default 2.
retryMinutesintegerNoWait before the next attempt: 1–1440, default 60.
callingHoursobjectNostart and end ("HH:MM", local time), days (MON…SUN) and skipDates (dates with no calls, such as public holidays). Default: your default calling hours.
startbooleanNotrue starts calling right away. Default false (created as a draft).
Request body
{
  "name": "October appointment reminders",
  "profile": "appointment-reminder",
  "contactIds": ["P-2291", "P-2307", "P-2315"],
  "maxAttempts": 3,
  "retryMinutes": 90,
  "callingHours": {
    "start": "09:00",
    "end": "18:00",
    "days": ["MON", "TUE", "WED", "THU", "FRI", "SAT"],
    "skipDates": ["2026-10-20"]
  },
  "start": true
}
Response · 201
{
  "id": "CAMP-6D2E91AF",
  "name": "October appointment reminders",
  "profile": "appointment-reminder",
  "channel": "phone",
  "status": "running",
  "totals": { "contacts": 3, "done": 0, "pending": 3, "calling": 0, "skipped": 0 },
  "createdAt": "2026-09-25T03:29:47Z"
}
GET/v1/campaigns/{campaignId}XURA GLOBAL

Campaign progress with one row per contact.

Response · 200 (shortened)
{
  "id": "CAMP-6D2E91AF",
  "status": "running",
  "totals": { "contacts": 3, "done": 1, "pending": 1, "calling": 1, "skipped": 0 },
  "byOutcome": { "RESCHEDULE_REQUESTED": 1 },
  "items": [
    { "contactId": "P-2291", "status": "done", "attempts": 1, "lastOutcome": "RESCHEDULE_REQUESTED", "lastCallId": "8d4a0f6e-…" },
    { "contactId": "P-2307", "status": "pending", "attempts": 1, "lastOutcome": "NO_ANSWER", "nextAttemptAt": "2026-09-25T06:32:41Z" },
    { "contactId": "P-2315", "status": "calling", "attempts": 1, "lastOutcome": null }
  ]
}
POST/v1/campaigns/{campaignId}/start | /pause | /stopXURA GLOBAL

Start a draft or paused campaign. Pause it (a call already in progress finishes). Stop it for good: contacts not yet called are skipped and campaign.completed is sent with status stopped.

Profiles

GET/v1/profilesXURA GLOBAL

List your call profiles with the context fields they need and the outcome codes and data they return. Useful for building forms and reports in your front end.

Response · 200 (one profile shown)
{
  "data": [
    {
      "id": "payment-reminder",
      "name": "Payment reminder",
      "directions": ["outbound", "inbound"],
      "contextFields": [
        { "name": "amountDue", "type": "number", "required": true },
        { "name": "currency", "type": "string", "required": true },
        { "name": "dueDate", "type": "date", "required": true }
      ],
      "outcomes": ["PROMISE_TO_PAY", "ALREADY_PAID", "FINANCIAL_HARDSHIP", "REFUSED_TO_PAY", "DISPUTED"],
      "captures": [
        { "name": "promisedDate", "type": "date" },
        { "name": "promisedAmount", "type": "number" },
        { "name": "paidDate", "type": "date" }
      ]
    }
  ],
  "nextCursor": null
}

Knowledge base

The agents answer questions about your products, services and procedures from these documents. Instruction documents tell the agents how to work (tone, limits, when to hand over to your staff) and are always used. Knowledge documents hold the facts they answer from. Changes apply from the next call.

POST/v1/knowledge/documentsXURA GLOBAL

Add a document as text (Markdown) or as a file: .md, .txt, .pdf or .docx, up to 10 MB, sent in base64.

As text
{
  "title": "ශාඛා සහ විවෘත වේලාවන් (Branches and opening hours)",
  "type": "knowledge",
  "language": "si",
  "content": "# සෙනසුරාදා\n- හැම ශාඛාවක්ම උදේ 9.00 සිට දවල් 1.00 දක්වා විවෘතයි…"
}
As a file
{
  "type": "knowledge",
  "fileName": "service-procedures.pdf",
  "fileBase64": "JVBERi0xLjQKJ…"
}
Response · 201
{
  "id": "DOC-4B7E19C2",
  "title": "ශාඛා සහ විවෘත වේලාවන් (Branches and opening hours)",
  "type": "knowledge",
  "language": "si",
  "enabled": true,
  "characters": 1320,
  "passages": 6,
  "updatedAt": "2026-09-25T04:10:12Z"
}
Writing good documents

Use short sections with headings, one fact per line and exact numbers. Write numbers the way they should be spoken, with digits in brackets: සියයට 2ක් (2%). Anything missing from the documents becomes a call-back request, so write down every answer you expect the agents to give.

POST/v1/knowledge/askXURA GLOBAL

Preview an answer. Useful after editing a document, and for a "test a question" box in your front end.

Request body → response · 200
{ "question": "සෙනසුරාදා ශාඛා ඇරලා තියෙනවද?" }

{
  "answer": "ඔව්. හැම ශාඛාවක්ම සෙනසුරාදා උදේ නවයේ ඉඳන් දවල් එක වෙනකම් ඇරලා තියෙනවා.",
  "passages": [
    { "documentId": "DOC-4B7E19C2", "title": "ශාඛා සහ විවෘත වේලාවන්", "heading": "සෙනසුරාදා", "score": 0.84 }
  ]
}

Webhook and settings

PUT/v1/webhookXURA GLOBAL

Set your webhook URL and, optionally, the events you want. POST /v1/webhook/test then sends a signed webhook.test event so you can check your endpoint.

Request body
{
  "url": "https://api.your-company.com/webhooks/xura",
  "events": ["call.completed", "call.not_connected", "campaign.completed"]
}
PUT/v1/settingsXURA GLOBAL

Agent settings. voice is one of the female voices Kore, Aoede, Leda, Zephyr or the male voices Charon, Orus, Puck, Fenrir. defaultCallingHours has the same shape as a campaign's callingHours and applies to campaigns that do not set their own. Until you set it, campaigns call 08:00–19:00, Monday to Saturday.

Request body
{
  "voice": "Kore",
  "defaultCallingHours": { "start": "08:00", "end": "19:00", "days": ["MON", "TUE", "WED", "THU", "FRI", "SAT"] }
}
GET/v1/eventsXURA GLOBAL · OPTIONAL

Live updates as a Server-Sent Events stream, for a front end that shows calls and transcripts while they happen. Open it from your server and forward it to your staff's screens. Events: call.started, call.transcript (one new line of the conversation), call.completed, call.not_connected.

Stream
event: call.transcript
data: {"callId":"3ec2bcda-…","entry":{"role":"contact","text":"ඔව්, නිමල් කතා කරන්නේ.","at":"2026-09-25T04:44:58Z"}}

Errors

Errors use standard HTTP status codes and this body:

Response · 404
{
  "error": {
    "code": "contact_not_found",
    "message": "Contact C-99999 was not found in your Contact API.",
    "requestId": "req_8f2c61d09a"
  }
}
StatusCodeMeaning
400invalid_requestA field is missing or invalid; message says which.
401unauthorizedMissing or wrong API key.
404not_found, contact_not_found, profile_not_foundThe call, campaign, document, contact or profile does not exist.
409conflictThe action does not fit the current state, for example starting a finished campaign.
413file_too_largeA document is larger than 10 MB.
422contact_not_callableThe contact has doNotCall: true.
422channel_not_enabledThe channel is not set up for your organization, for example WhatsApp.
429rate_limitedToo many requests; wait for Retry-After seconds.
502contact_api_errorYour Contact API did not answer correctly in time.
503line_unavailableThe phone line is down. Nothing was dialled; try again later.

Quote the requestId when you contact us about an error.

Your team, your telecom operator and Xura Global4Phone line (SIP trunk)

The agents make and receive ordinary phone calls over a SIP trunk: phone lines that a licensed telecom operator delivers over an IP network. Your organization orders the trunk and its numbers from the operator and keeps that contract and its call charges. Xura Global connects the trunk to the telephony gateway in your system.

Connection options

Phone line connection options Option A connects the operator's SIP trunk directly to the gateway in your Xura system. Option B passes calls through your IP-PBX. Option C uses a gateway at your premises and an encrypted VPN tunnel. A Telecom operator SIP trunk over the internet Your Xura system gateway and AI agents B Telecom operator SIP trunk Your IP-PBX existing phone system SIP Your Xura system gateway and AI agents C Telecom operator SIP trunk On-site gateway at your premises VPN tunnel Your Xura system gateway and AI agents
Three ways to connect a SIP trunk. The agents work the same way with each of them.

ADirect SIP trunk

How it works
Your operator connects the trunk over the internet straight to your system's gateway.
Choose it when
The numbers are used only by the agents, and your operator offers SIP trunks over the internet.
You need
Nothing on site.

BThrough your phone system

How it works
Your IP-PBX keeps the operator trunk. A SIP trunk between the PBX and your Xura system carries the calls you route to the agents and the agents' outbound calls.
Choose it when
You have a PBX or call centre, want to keep your numbers, or want the agents to take only some calls, such as after hours or one IVR option.
You need
Your PBX administrator to add a SIP trunk and routes.

CThrough a secure tunnel

How it works
A small gateway at your premises takes the operator trunk and connects to the agents over an encrypted VPN.
Choose it when
Your operator delivers trunks only to your premises over a private line, and you have no PBX.
You need
A small server or virtual machine on your network, specified by Xura Global.
Before the trunk is ready

Xura Global gives your team test phone accounts (an app on a phone or computer) connected to the gateway. Every test except the phone line tests can start before the trunk is connected.

Requirements

ItemRequirement
SignallingSIP (RFC 3261) over UDP, TCP or TLS. TLS is recommended where your operator supports it.
AudioRTP, or SRTP where supported. Codecs: G.711 A-law or µ-law (required), G.722 (optional).
Keypad tonesRFC 4733 (RFC 2833) telephone events.
AuthenticationIP allowlisting of your system's gateway (preferred), or registration with a username and password.
Number formatE.164 preferred. National formats are converted by the gateway.
Caller IDOutbound calls show a number your operator has assigned to your organization.
ChannelsEnough simultaneous calls for your busiest hour, inbound and outbound together.
Network qualityAbout 100 kbit/s each way per call (G.711). Latency below 150 ms, jitter below 30 ms, packet loss below 1%.

What each side provides

Your team provides

  • Operator name and the chosen option (A, B or C)
  • SIP server addresses, port and transport
  • Authentication: IP allowlisting, or a username and password sent securely
  • The numbers routed to the agents, and the caller ID for outbound calls
  • Number of channels, codecs and dialling format
  • The operator's signalling and audio IP ranges
  • The destinations the agents may call, for example Sri Lankan numbers only

Xura Global provides

  • Gateway address and SIP port
  • Public IP addresses for your operator's or PBX's allowlist
  • Audio (RTP) port range
  • Test phone accounts
  • For option C, the gateway specification and setup guide

Routing and handover

  • Inbound. Every call to a number routed to the agents is answered by an agent, day and night. To send only some calls, route them in your PBX (option B).
  • Outbound. Calls show your caller ID. Campaign calls are placed only within your calling hours; calls you start one by one through the Voice API are placed straight away.
  • Handover to staff. In version 1 the agents do not transfer live calls. When a contact needs a person, the agent promises a call back and the result arrives as CALLBACK_REQUESTED with the reason.
Regulation

Use numbers and trunks from a licensed telecom operator and follow your telecom regulator's rules (in Sri Lanka, the TRCSL). Xura Global does not connect SIM-based GSM gateways ("SIM boxes") for business calling: operators block them and they can breach telecom rules.

OptionalWhatsApp calling

Customers can also call your organization on WhatsApp, and the agents can call customers on WhatsApp once they allow it. WhatsApp is an extra line, not a separate system: the calls reach the same agents, use the same Contact API and send the same webhook events, with channel: "whatsapp".

How it connects

Meta connects WhatsApp calls for your business number to the gateway in your system over SIP, using the WhatsApp Business Calling API. Meta requires encrypted signalling (SIP over TLS) and encrypted audio (SRTP); the gateway handles both.

What you need

  • A WhatsApp Business Platform (Cloud API) account for your organization. Numbers used in the WhatsApp Business app cannot be used.
  • The WhatsApp number your customers will call, enabled for calling. Meta requires the number to have a messaging limit of at least 2,000 unique recipients a day.
  • Access for Xura Global to set up calling on that number in your Meta business account, or your WhatsApp provider points the number's SIP calling to the address we give you. A number has one SIP destination for calls; your WhatsApp messages are not affected.
  • For outbound calls: an approved message template that asks customers for permission to call.

Meta's rules for WhatsApp calls

RuleDetail
Customer calls youAvailable wherever WhatsApp Business calling is offered. Free of charge.
You call a customerOnly after the customer gives permission in reply to a WhatsApp message. Not available in the USA, Canada, Egypt, Vietnam or Nigeria.
PermissionTemporary (7 days) or permanent, as the customer chooses. A customer who calls you gives temporary permission automatically.
Asking for permissionAt most 1 request in 24 hours and 2 in 7 days for each customer.
Call limitUp to 100 connected calls with each customer in 24 hours.
Unanswered callsAfter 4 unanswered calls in a row, the permission is withdrawn.
ChargesCalls from customers are free. Calls you make are charged by Meta in 6-second steps, by destination country. Permission requests are charged as messages.

Meta changes these rules from time to time. The current rules are in Meta's WhatsApp Business Calling documentation.

In the API

  • Inbound. We look up the caller's WhatsApp number with your Contact API as usual. If WhatsApp does not share the caller's number, the caller is treated as unknown.
  • Outbound. POST /v1/calls or POST /v1/campaigns with channel: "whatsapp". A contact who has not given permission is reported as call.not_connected with FAILED and end reason no_whatsapp_permission, and is not retried.
  • Permission requests are sent from your WhatsApp Business account with your approved template, by Xura Global on your behalf or by your own WhatsApp system.

Security and data protection

Credentials

  • The Voice API key and the webhook signing secret (from Xura Global), the Contact API key (from you) and, if your trunk uses registration, the SIP password.
  • Your Google Cloud keys are stored only on your own system.
  • Keep them in a secrets store on your servers, never in front-end code, email or chat.
  • Rotate them every 90 days and whenever someone with access leaves.

Network and phone line

  • HTTPS with TLS 1.2 or later in both directions.
  • You allow only your system's address on your Contact API and webhook; on request, your Voice API accepts requests only from your servers.
  • The SIP trunk accepts traffic only from agreed addresses, and outbound calls go only to the destinations agreed at onboarding, which protects your trunk from toll fraud.

Personal data

  • Your system is dedicated to your organization: data, keys and phone lines are never shared with other organizations.
  • The Contact API returns only the fields in this guide, and context holds only what the call needs.
  • Transcripts contain personal information: store them encrypted and limit who can read them.
  • Version 1 keeps transcripts, not audio recordings.

Compliance

  • Speech recognition and the AI model run in your organization's own Google Cloud project, so call data may be processed outside your country. This and the retention period are set out in the data processing agreement under the data protection law that applies to you (in Sri Lanka, the Personal Data Protection Act, No. 9 of 2022).
  • The agents never call contacts marked doNotCall, report opt-out requests, and campaigns call only within your calling hours.

Onboarding checklist

Both teams exchange the following before testing starts. Send secrets through a password manager or an encrypted channel, never by plain email.

Your team sends Xura Global

  • Completed call profile sheet for each profile
  • ① Contact API base URL and API key, for testing and for production
  • ② Webhook URL, for testing and for production
  • Test contacts: IDs and your staff's phone numbers
  • ④ SIP trunk details (see what each side provides)
  • Google Cloud project with billing enabled, and access for Xura Global to create the keys
  • Server: a VPS in your name with administrator (SSH) access for Xura Global, or a request for hosting by Xura Global
  • Your server addresses, if we should restrict the Voice API to them
  • Knowledge documents
  • Optional: WhatsApp Business account access and number
  • A technical contact and an escalation contact

Xura Global sends your team

  • ③ Your system's address (Voice API base URL) and API keys (xv_test_…, xv_live_…)
  • ② Webhook signing secret for each environment (whsec_…)
  • Your system's IP address, to allow on your firewall
  • ④ Gateway address, ports and public IP addresses
  • Test phone accounts, so your team can play the contact
  • A technical contact and an escalation contact
TestingProduction
Voice API addressThe same for both: your system's base URL
Voice API keyxv_test_…xv_live_…
Phone lineTest phone accounts, then your trunk with your staff's phones as test contactsYour trunk and numbers
Contact dataYour test Contact API with test contactsYour production Contact API
WebhookYour test endpointYour production endpoint

Testing and go-live

Acceptance tests

We run these together in the testing environment. Each must pass before go-live; test 15 applies only if you use WhatsApp.

#TestExpected result
Data and results
1① Look up a test contact's phone number200 with the contact object, in under 800 ms
2① Look up an unknown number; call with a wrong key404; 401
3② POST /v1/webhook/testYour endpoint verifies the signature and answers 200
4② The same test event with a changed bodyYour endpoint answers 401
5② The same event sent twiceStored and processed once
Calls
6③ Call a test contact and complete the profile's main goalcall.started, then call.completed with the expected outcome code and data
7③ Call a test contact and do not answercall.not_connected with NO_ANSWER after about 30 seconds
8④ Call your number from a test contact's phoneThe agent answers and addresses the contact by name after confirming it
9④ Outbound call through the trunkThe phone shows your caller ID; clear audio both ways
10④ As many calls at once as your trunk has channelsAll calls connect with clear audio
Conversation
11Ask a question from the knowledge base, then one that is not in itA correct answer; then a call-back promise (CALLBACK_REQUESTED)
12Say "please don't call me again"The call ends politely with OPT_OUT
Campaigns and rules
13③ Campaign of three test contacts with one retryOne event per attempt, then campaign.completed
14A test contact with doNotCall: trueNot called: 422 for a single call, CANCELED in a campaign
Optional
15WhatsApp: call your WhatsApp number; then an outbound WhatsApp call after permissionThe agent answers; the outbound call connects

Go-live checklist

  • All acceptance tests passed and signed off by both teams
  • Call profiles and knowledge documents approved by your business and compliance teams
  • Production keys and the signing secret exchanged and stored in secrets stores
  • Firewalls allow your system's address; production webhook URL set
  • Google Cloud billing enabled and a monthly budget alert set
  • Production trunk: numbers routed to the agents, caller ID and allowed destinations checked
  • Calling hours, holidays and do-not-call flags checked; opt-outs update your CRM
  • Pilot with a small group of contacts, reviewed before the full rollout
  • Monitoring agreed: failed webhooks, Contact API errors and phone line faults alert both teams

Reference codes

Standard outcome codes

Used by every call profile.

CodeMeaningSuggested follow-up on your side
CALLBACK_REQUESTEDThe contact asked for a person to call back, or asked something the agent could not answerYour staff call back; add missing answers to the knowledge base
OPT_OUTThe contact asked not to be called againSet doNotCall in your CRM
WRONG_PERSONSomeone else answered, or the number does not belong to the contactCheck the contact details
NO_RESPONSEThe call connected but nobody spokeTry again later
INCOMPLETEThe call ended before a result was reachedTry again later
NO_ANSWERNot connected: nobody answered within 30 secondsRetried automatically in campaigns
BUSYNot connected: busy or declinedRetried automatically in campaigns
FAILEDNot connected: the number could not be reached, contact data was not available, or there was no WhatsApp permissionCheck the number, the Contact API or the permission
CANCELEDNot connected: cancelled by your system, or the contact is marked do-not-call—

Profile outcome codes (examples)

CodeMeaningCaptured data
Payment reminder
PROMISE_TO_PAYAgreed to pay an amount on a date, and confirmed itpromisedDate, promisedAmount
ALREADY_PAIDSays the amount has already been paidpaidDate, if given
FINANCIAL_HARDSHIPCannot pay now (job loss, illness, business losses)—
REFUSED_TO_PAYRefused to pay—
DISPUTEDDisputes the amount or the debt—
Appointment reminder
CONFIRMEDWill attend as booked—
RESCHEDULE_REQUESTEDWants another date or timerequestedDate
CANCELLEDWill not attend—
Order and delivery confirmation
CONFIRMEDConfirms the order and the delivery—
CHANGE_REQUESTEDWants to change the address, date or itemschangeNote
CANCELLEDCancels the order—
Lead follow-up
INTERESTEDWants to hear more or to meetproductOfInterest, preferredTime
NOT_INTERESTEDIs not interested—
Customer service line
RESOLVEDThe question was answered from the knowledge basetopic
Feedback survey
SURVEY_COMPLETEDAnswered the surveyscore, comments
DECLINEDDid not want to take part—

End reasons

CodeMeaning
agent_completedThe agent finished the conversation and said goodbye
contact_hangupThe contact hung up
ended_by_operatorEnded through POST /v1/calls/{callId}/cancel
max_durationReached the 10-minute limit
technical_errorA technical problem with the phone audio or the AI service ended the call; the outcome is usually INCOMPLETE
no_answer, busy, failed, canceledNot connected (see the outcome code)
do_not_call, contact_not_found, contact_api_error, no_whatsapp_permissionNot placed, for the reason named

Call status

queued → dialing → in_progress → completed. A call that is not answered goes from dialing to not_connected. Inbound calls start at in_progress.

Campaign status

draft → running ⇄ paused → completed or stopped. A campaign completes when every contact has a final result.

Each contact in a campaign is pending (waiting for its first call or a retry), calling, done (has a final result) or skipped (not called: do-not-call, not found in your Contact API, or no WhatsApp permission).

Document history

VersionDateChanges
1.025 September 2026First release