Kaphila API Documentation

Complete reference with multi-language examples in Python, Node.js, PHP, Java, C#, and cURL.

Authentication

All API requests must include an Authorization header with your API key.

Header
Authorization: Bearer ak_your_api_key_here
Get Your API Key
  1. Log in to your Dashboard
  2. Click "Generate Key"
  3. Copy the key and use it in your requests

Make a Call — POST /makecall

Initiate an outbound call to a phone number.

Important:
  • Phone numbers must be in 10-15 digit format WITHOUT the + prefix (e.g., 16089270862).
  • callerId must be a verified/whitelisted number from your account. Invalid or unverified numbers will be ignored or cause failure.
Request Body
{
  "number": "16089270862",
  "webhookUrl": "https://your-server.com/webhook",
  "useAmd": true,
  "callerId": "19876543210",  // Must be whitelisted
  "voiceName": "en-US-Neural2-A",
  "ringTimeout": 30
}
Response
{
  "success": true,
  "callId": "1758549398.181",
  "status": "ringing",
  "amdEnabled": true,
  "voiceName": "en-US-Neural2-A",
  "credits": 10,
  "ringTimeoutSeconds": 30,
  "timestamp": "2025-09-22T13:56:38.365Z"
}

Forward a Call — POST /forwardcall

Forward an active call to another number.

Request Body
{
  "callId": "1758549398.181",
  "forwardNumber": "19876543210"
}
Response
{
  "success": true,
  "message": "Call forwarded to 19876543210",
  "forwardedCallId": "1758549447.186"
}

Text-to-Speech — POST /voice

Play synthesized speech to a call.

Request Body
{
  "callId": "1758549398.181",
  "text": "Hello, this is a test message.",
  "playTo": "bridge"
}
Response
{
  "success": true,
  "message": "TTS played successfully",
  "playbackId": "tts-1758549398.181-123456"
}

Gather DTMF — POST /gather

Prompt user for DTMF input.

Request Body
{
  "callId": "1758549398.181",
  "text": "Press 1 for sales, 2 for support.",
  "numDigits": 1,
  "timeout": 10000
}
Webhook Events
  • gather.started
  • gather.progress
  • gather.complete
  • gather.timeout

Webhooks

Receive real-time events about your calls.

Events
  • call.initiated
  • call.answered
  • call.ended
  • call.timeout
  • dtmf.received
  • gather.*
  • recording.*
Example Webhook Payload
{
  "callId": "1758549398.181",
  "event": "call.answered",
  "status": "answered",
  "amd": {
    "status": "HUMAN",
    "confidence": 0.9
  },
  "timestamp": "2025-09-20T19:44:29Z"
}

Code Examples — Make a Call

Examples in multiple languages. Replace ak_your_api_key_here and URLs with your own.

curl -X POST "https://kaphila.com/makecall" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "number": "16089270862",
    "callerId": "19876543210",  // Must be whitelisted
    "webhookUrl": "https://kaphila.com/call-handler",
    "useAmd": true,
    "voiceName": "en-US-Neural2-A",
    "ringTimeout": 30
  }'
import requests

API_KEY = "ak_your_api_key_here"
BASE_URL = "https://kaphila.com"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

data = {
    "number": "16089270862",      # No '+' prefix
    "callerId": "19876543210",    # Must be whitelisted
    "webhookUrl": "https://kaphila.com/call-handler",
    "useAmd": True,
    "voiceName": "en-US-Neural2-A",
    "ringTimeout": 30
}

response = requests.post(f"{BASE_URL}/makecall", json=data, headers=headers)
print(response.json())
const axios = require('axios');

const API_KEY = 'ak_your_api_key_here';
const BASE_URL = 'https://kaphila.com';

const data = {
  number: '16089270862',
  callerId: '19876543210',      // Must be whitelisted
    webhookUrl: 'https://kaphila.com/call-handler',
  useAmd: true,
  voiceName: 'en-US-Neural2-A',
  ringTimeout: 30
};

axios.post(`${BASE_URL}/makecall`, data, {
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json'
  }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
 '16089270862',
    'callerId' => '19876543210',    // Must be whitelisted
    'webhookUrl' => 'https://kaphila.com/call-handler',
    'useAmd' => true,
    'voiceName' => 'en-US-Neural2-A',
    'ringTimeout' => 30
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $apiKey,
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
?>
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class MakeCall {
    public static void main(String[] args) throws Exception {
        String apiKey = "ak_your_api_key_here";
        String url = "https://kaphila.com/makecall";

        String json = """
            {
              "number": "16089270862",
              "callerId": "19876543210",  // Must be whitelisted
              "webhookUrl": "https://kaphila.com/call-handler",
              "useAmd": true,
              "voiceName": "en-US-Neural2-A",
              "ringTimeout": 30
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
                .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class CallRequest
{
    public string number { get; set; }
    public string callerId { get; set; }     // Must be whitelisted
    public string webhookUrl { get; set; }
    public bool useAmd { get; set; }
    public string voiceName { get; set; }
    public int ringTimeout { get; set; }
}

public class Program
{
    public static async Task Main()
    {
        var client = new HttpClient();
        client.DefaultRequestHeaders.Add("Authorization", "Bearer ak_your_api_key_here");

        var request = new CallRequest
        {
            number = "16089270862",
            callerId = "19876543210",        // Must be whitelisted
            webhookUrl = "https://kaphila.com/call-handler",
            useAmd = true,
            voiceName = "en-US-Neural2-A",
            ringTimeout = 30
        };

        var json = JsonConvert.SerializeObject(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await client.PostAsync("https://kaphila.com/makecall", content);
        var result = await response.Content.ReadAsStringAsync();

        Console.WriteLine(result);
    }
}

IVR Demos — Interactive Voice Response

Combine /voice and /gather to build powerful IVR menus. Below are complete, real-world examples.

All IVR flows start with a call initiated via /makecall. After the call is answered, use /voice to speak and /gather to collect input.
1. “Press 1 for Sales, 2 for Support”

Play prompt → Gather 1 digit → Route based on input.

# Step 1: Play prompt
curl -X POST "https://kaphila.com/voice" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "callId": "1758549398.181",
    "text": "Welcome to our company. Press 1 for Sales, 2 for Support.",
    "playTo": "bridge"
  }'

# Step 2: Gather input
curl -X POST "https://kaphila.com/gather" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "callId": "1758549398.181",
    "numDigits": 1,
    "timeout": 10000
  }'
import requests

API_KEY = "ak_your_api_key_here"
BASE_URL = "https://kaphila.com"
CALL_ID = "1758549398.181"

headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

# Step 1: Speak menu
requests.post(f"{BASE_URL}/voice", json={
    "callId": CALL_ID,
    "text": "Welcome to our company. Press 1 for Sales, 2 for Support.",
    "playTo": "bridge"
}, headers=headers)

# Step 2: Gather input
response = requests.post(f"{BASE_URL}/gather", json={
    "callId": CALL_ID,
    "numDigits": 1,
    "timeout": 10000
}, headers=headers)

print("Gather started:", response.json())
const axios = require('axios');

const API_KEY = 'ak_your_api_key_here';
const BASE_URL = 'https://kaphila.com';
const CALL_ID = '1758549398.181';

const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
};

// Step 1: Speak menu
await axios.post(`${BASE_URL}/voice`, {
  callId: CALL_ID,
  text: "Welcome to our company. Press 1 for Sales, 2 for Support.",
  playTo: "bridge"
}, { headers });

// Step 2: Gather input
const res = await axios.post(`${BASE_URL}/gather`, {
  callId: CALL_ID,
  numDigits: 1,
  timeout: 10000
}, { headers });

console.log("Gather started:", res.data);
Expected Webhook Events
{
  "event": "gather.complete",
  "callId": "1758549398.181",
  "digits": "1",
  "timestamp": "2025-09-22T14:30:00Z"
}
💡 Logic Tip: On your webhook server, check digits:
  • if digits == "1" → Transfer to sales
  • if digits == "2" → Transfer to support
  • else → Replay menu or say “Invalid option”
2. “Please enter your 6-digit PIN code”

Useful for authentication or account access.

curl -X POST "https://kaphila.com/voice" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -d '{
    "callId": "1758549398.181",
    "text": "Please enter your 6-digit PIN code, followed by the pound key.",
    "playTo": "bridge"
  }'

curl -X POST "https://kaphila.com/gather" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -d '{
    "callId": "1758549398.181",
    "numDigits": 6,
    "finishOnKey": "#",
    "timeout": 15000
  }'
requests.post(f"{BASE_URL}/voice", json={
    "callId": CALL_ID,
    "text": "Please enter your 6-digit PIN code, followed by the pound key.",
    "playTo": "bridge"
}, headers=headers)

requests.post(f"{BASE_URL}/gather", json={
    "callId": CALL_ID,
    "numDigits": 6,
    "finishOnKey": "#",
    "timeout": 15000
}, headers=headers)
Webhook Payload on Success
{
  "event": "gather.complete",
  "callId": "1758549398.181",
  "digits": "123456",
  "timestamp": "2025-09-22T14:31:00Z"
}
3. “Say YES to confirm, NO to cancel”

Use speech recognition (if supported) or map keypad: YES=1, NO=2.

curl -X POST "https://kaphila.com/voice" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -d '{
    "callId": "1758549398.181",
    "text": "To confirm your appointment, press 1. To cancel, press 2.",
    "playTo": "bridge"
  }'

curl -X POST "https://kaphila.com/gather" \
  -H "Authorization: Bearer ak_your_api_key_here" \
  -d '{
    "callId": "1758549398.181",
    "numDigits": 1,
    "validDigits": "12",
    "timeout": 10000
  }'
Handle in Webhook
// Pseudo-code for your webhook endpoint
if (event === 'gather.complete') {
    if (digits === '1') {
        playTTS("Confirmed! Thank you.");
        // Trigger business logic: send email, update DB, etc.
    } else if (digits === '2') {
        playTTS("Cancelled. Goodbye.");
        hangupCall();
    } else {
        playTTS("Invalid input. Goodbye.");
        hangupCall();
    }
}
Pro Tip: Chain multiple /voice and /gather calls to build complex IVR trees. Always handle gather.timeout and gather.failed events for fallbacks.