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
Sign in to your account
Log in to your kalem.me dashboard at https://kalem.me/login
-
2
Navigate to Settings
Click on your profile icon in the top right corner and select "Settings" from the dropdown menu.
-
3
Go to API Tokens section
In the settings page, find and click on the "API Tokens" tab in the left sidebar.
-
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
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
/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
/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.