API Documentation

Welcome to the kalem.me API documentation. Our REST API allows you to programmatically create and manage AI voice calls, integrate voice agents into your applications, and access call analytics.

Base URL: https://kalem.me/api

Authentication

The kalem.me API uses Bearer token authentication. Include your API token in the Authorization header of each request.

Authorization: Bearer YOUR_API_TOKEN

All API requests must be made over HTTPS. Requests without authentication will fail with a 401 Unauthorized response.

Getting Your API Token

To use the kalem.me API, you need to generate an API token from your dashboard. Follow these steps:

  1. 1

    Sign in to your account

    Log in to your kalem.me dashboard at https://kalem.me/login

  2. 2

    Navigate to Settings

    Click on your profile icon in the top right corner and select "Settings" from the dropdown menu.

  3. 3

    Go to API Tokens section

    In the settings page, find and click on the "API Tokens" tab in the left sidebar.

  4. 4

    Create a new token

    Click the "Create New Token" button, give your token a descriptive name (e.g., "Production API"), and select the appropriate permissions.

  5. 5

    Copy and secure your token

    Your token will be displayed once. Copy it and store it securely. You won't be able to see it again!

Important: Keep your API tokens secure and never share them publicly. Treat them like passwords. If a token is compromised, revoke it immediately and generate a new one.

Calls API

The Calls API allows you to programmatically create outbound calls using your AI voice agents and retrieve call details and analytics.

Create Call

POST /api/calls

Initiates an outbound call to one or more phone numbers using your configured AI agent and phone number. You can create a single call by providing a string, or multiple calls simultaneously by providing an array of phone numbers.

Multiple Calls: When you provide an array of phone numbers, a separate call will be created for each number with a unique call_id. Each call will use the same agent, number, and trunk configuration.

Trunk Requirement: The trunk_id is required when calling E.164 phone numbers (e.g., +15551234567) but optional for SIP URIs (e.g., sip:1001@sip.kalem.me).

Phone Number Formatting: E.164 phone numbers can be provided in various formats like "+1 (863) 990-1708", "+1-555-123-4567", or "+15551234567". Spaces, hyphens, and parentheses are automatically cleaned before saving.

Request Headers

Header Value
Content-Type application/json
Authorization Bearer YOUR_API_TOKEN

Request Body Parameters

Parameter Type Required Description
to_number string or array Required Destination phone number(s) in E.164 format (e.g., "+15551234567" or "+1 (555) 123-4567") or SIP URI format (e.g., "sip:1001", "sip:1001@sip.kalem.me"). Phone numbers can include spaces, hyphens, and parentheses - they will be cleaned automatically. Can be a single string or an array of strings to create multiple calls simultaneously
agent_id integer Required ID of the AI agent to use for the call(s)
number_id integer Required ID of the phone number to call from (must be active)
trunk_id integer Required for E.164 Optional for SIP ID of the SIP trunk to use (must be active). Required when calling E.164 phone numbers, optional for SIP URIs
record boolean Optional Whether to record the call(s) (default: true)
metadata object Optional Custom metadata to attach to the call(s)

Example Request (Single Call)

curl -X POST https://kalem.me/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "to_number": "+15551234567",
    "agent_id": 1,
    "number_id": 1,
    "trunk_id": 1,
    "record": true
  }'

Example Request (Formatted Phone Number)

curl -X POST https://kalem.me/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "to_number": "+1 (863) 990-1708",
    "agent_id": 1,
    "number_id": 1,
    "trunk_id": 1,
    "record": true
  }'

Example Request (Multiple Calls)

curl -X POST https://kalem.me/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "to_number": ["+15551234567", "+15559876543", "+15558889999"],
    "agent_id": 1,
    "number_id": 1,
    "trunk_id": 1,
    "record": true
  }'

Example Request (SIP URI Call)

curl -X POST https://kalem.me/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "to_number": "sip:1001@sip.kalem.me",
    "agent_id": 1,
    "number_id": 1,
    "record": true
  }'

Example Request (Mixed Phone and SIP URIs)

curl -X POST https://kalem.me/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
    "to_number": ["+15551234567", "sip:1001", "sip:support@sip.kalem.me"],
    "agent_id": 1,
    "number_id": 1,
    "trunk_id": 1,
    "record": true
  }'

Example Request (JavaScript - Single Call)

const response = await fetch('https://kalem.me/api/calls', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_TOKEN'
  },
  body: JSON.stringify({
    to_number: '+15551234567',
    agent_id: 1,
    number_id: 1,
    trunk_id: 1,
    record: true
  })
});

const data = await response.json();
console.log(data);

Example Request (JavaScript - Multiple Calls)

const response = await fetch('https://kalem.me/api/calls', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_TOKEN'
  },
  body: JSON.stringify({
    to_number: ['+15551234567', '+15559876543'],
    agent_id: 1,
    number_id: 1,
    trunk_id: 1,
    record: true
  })
});

const data = await response.json();
console.log(data);

Example Request (Python - Single Call)

import requests

url = 'https://kalem.me/api/calls'
headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_TOKEN'
}
payload = {
    'to_number': '+15551234567',
    'agent_id': 1,
    'number_id': 1,
    'trunk_id': 1,
    'record': True
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())

Example Request (Python - Multiple Calls)

import requests

url = 'https://kalem.me/api/calls'
headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_TOKEN'
}
payload = {
    'to_number': ['+15551234567', '+15559876543'],
    'agent_id': 1,
    'number_id': 1,
    'trunk_id': 1,
    'record': True
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())

Example Request (PHP - Single Call)

$ch = curl_init('https://kalem.me/api/calls');

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_API_TOKEN'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'to_number' => '+15551234567',
    'agent_id' => 1,
    'number_id' => 1,
    'trunk_id' => 1,
    'record' => true
]));

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

$data = json_decode($response, true);
print_r($data);

Example Request (PHP - Multiple Calls)

$ch = curl_init('https://kalem.me/api/calls');

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_API_TOKEN'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'to_number' => ['+15551234567', '+15559876543'],
    'agent_id' => 1,
    'number_id' => 1,
    'trunk_id' => 1,
    'record' => true
]));

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

$data = json_decode($response, true);
print_r($data);

Success Response - Single Call (201 Created)

{
  "message": "Call initiated successfully",
  "data": {
    "call_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "pending",
    "to_number": "+15551234567",
    "from_number": "+15559876543",
    "agent": "Customer Support Agent"
  }
}

Success Response - Multiple Calls (201 Created)

{
  "message": "3 calls initiated successfully",
  "data": [
    {
      "call_id": "550e8400-e29b-41d4-a716-446655440001",
      "status": "pending",
      "to_number": "+15551234567",
      "from_number": "+15559876543",
      "agent": "Customer Support Agent"
    },
    {
      "call_id": "550e8400-e29b-41d4-a716-446655440002",
      "status": "pending",
      "to_number": "+15559876543",
      "from_number": "+15559876543",
      "agent": "Customer Support Agent"
    },
    {
      "call_id": "550e8400-e29b-41d4-a716-446655440003",
      "status": "pending",
      "to_number": "+15558889999",
      "from_number": "+15559876543",
      "agent": "Customer Support Agent"
    }
  ]
}

Response Fields

Field Type Description
message string Success message indicating how many calls were created
data object or array Single call object (for single to_number) or array of call objects (for multiple to_number values)
call_id string (UUID) Unique identifier for the call
status string Current call status (typically "pending" when first created)
to_number string Destination phone number
from_number string Your phone number used for the call
agent string Name of the AI agent handling the call

Error Responses

422 Unprocessable Entity - Invalid phone number format

{
  "message": "Each phone number must be in E.164 format (e.g., +15551234567 or +1 (555) 123-4567) or SIP URI format (e.g., sip:1001 or sip:1001@sip.kalem.me).",
  "errors": {
    "to_number.0": [
      "Each phone number must be in E.164 format (e.g., +15551234567 or +1 (555) 123-4567) or SIP URI format (e.g., sip:1001 or sip:1001@sip.kalem.me)."
    ]
  }
}

422 Unprocessable Entity - Missing trunk for E.164 number

{
  "message": "A trunk is required when calling E.164 phone numbers.",
  "errors": {
    "trunk_id": [
      "A trunk is required when calling E.164 phone numbers."
    ]
  }
}

401 Unauthorized - Invalid or missing API token

{
  "message": "Unauthenticated."
}

403 Forbidden - Resource doesn't belong to your account

{
  "message": "This action is unauthorized."
}

404 Not Found - Agent or number not found

{
  "message": "The selected agent does not exist.",
  "errors": {
    "agent_id": [
      "The selected agent does not exist."
    ]
  }
}

Get Call Details

GET /api/calls/{callId}

Retrieves detailed information about a specific call, including duration, cost, status, and recording URL.

URL Parameters

Parameter Type Description
callId string (UUID) The unique identifier of the call

Example Request

curl -X GET https://kalem.me/api/calls/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Example Request (JavaScript)

const callId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(`https://kalem.me/api/calls/${callId}`, {
  headers: {
    'Authorization': 'Bearer YOUR_API_TOKEN'
  }
});

const data = await response.json();
console.log(data);

Success Response (200 OK)

{
  "data": {
    "call_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "to_number": "+15551234567",
    "from_number": "+15559876543",
    "agent": "Customer Support Agent",
    "duration_seconds": 245,
    "duration_minutes": 4.08,
    "cost": 0.98,
    "realtime_cost": 0.82,
    "started_at": "2024-12-10T14:30:00+00:00",
    "answered_at": "2024-12-10T14:30:03+00:00",
    "ended_at": "2024-12-10T14:34:08+00:00",
    "recording_url": "https://storage.kalem.me/recordings/550e8400-e29b-41d4-a716-446655440000.mp3",
    "hangup_cause": "NORMAL_CLEARING"
  }
}

Response Fields

Field Type Description
call_id string (UUID) Unique identifier for the call
status string Current call status: pending, ringing, in_progress, completed, failed, no_answer, busy
to_number string Destination phone number
from_number string Your phone number used for the call
agent string Name of the AI agent handling the call
duration_seconds integer Call duration in seconds (null if not answered)
duration_minutes float Call duration in minutes (null if not answered)
cost float Total cost in USD (null if not completed)
realtime_cost float Real-time AI processing cost in USD (null if not completed)
started_at string (ISO 8601) When the call was initiated (null if not started)
answered_at string (ISO 8601) When the call was answered (null if not answered)
ended_at string (ISO 8601) When the call ended (null if still in progress)
recording_url string (URL) URL to download call recording (null if recording disabled or not available)
hangup_cause string Reason for call termination (null if still in progress)

Error Handling

The kalem.me API uses conventional HTTP response codes to indicate the success or failure of requests.

HTTP Status Codes

Code Status Description
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request parameters
401 Unauthorized Invalid or missing API token
403 Forbidden Not authorized to access resource
404 Not Found Resource not found
422 Unprocessable Entity Validation failed
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error occurred

All errors include a message field with a human-readable description. Validation errors (422) also include an errors object with field-specific error messages.

Rate Limits

API requests are rate limited to ensure fair usage and system stability. The current limits are:

  • 60 requests per minute per API token
  • 1,000 requests per hour per API token

When you exceed the rate limit, you'll receive a 429 Too Many Requests response. The response headers include:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1702224180

The X-RateLimit-Reset header contains a Unix timestamp indicating when the rate limit will reset.

If you need higher rate limits for your application, please contact our support team to discuss enterprise options.

Need Help?

Our support team is here to help you integrate the kalem.me API into your application.