Click-to-Call
The Click-to-Call feature allows you to initiate outbound calls directly from your application. When triggered, the employee's phone rings first, and once they answer, the system dials the target number.
Required Scopes
calls.create— Required to initiate callsphones.read— Required to list available phones
How It Works
List Available Phones
Before initiating a call, you need to know which phone lines are available:
- cURL
- Python
curl "https://app.sipsim.com/api/v2/phones?status=active" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
import requests
access_token = "YOUR_ACCESS_TOKEN"
response = requests.get(
"https://app.sipsim.com/api/v2/phones",
headers={"Authorization": f"Bearer {access_token}"},
params={"status": "active"}
)
phones = response.json()["data"]
for phone in phones:
print(f"ID: {phone['id']} - {phone['number']} ({phone['name']})")
Phone Object
{
"id": 123,
"name": "Sales Department Phone",
"status": "active",
"internal_number": 101,
"number": "+33612345678",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-06-20T14:45:00Z",
"group": {
"group_id": 5,
"group_title": "Sales Team"
},
"outbound_setting": {
"outbound_number": "+33987654321",
"use_different_outbound_number": false,
"outbound_number_phone_id": null
},
"record_setting": {
"record_inbound_call": true,
"record_outbound_call": true
},
"voicemail_setting": {
"allow_voicemail": true,
"voicemail_file_id": null
},
"forwarding_setting": {
"forced": {
"enabled": false,
"forward_number": null
},
"conditional": {
"enabled": true,
"timeout_seconds": 25,
"forward_number": "+33555888999"
}
},
"audio_setting": {
"inbound_call_preroll_id": null,
"outbound_call_preroll_id": null,
"no_answer_file_id": null,
"wait_preroll_dial": 0
}
}
Phone Fields
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier of the phone |
name | string | Name or label of the phone |
status | string | Status: not_activated, active, inactive |
internal_number | integer | Internal extension number (100-999) |
number | string | Phone number (E.164 format) |
created_at | string | When the phone was created (ISO 8601) |
updated_at | string | When the phone was last updated (ISO 8601) |
group | object | Assigned group (group_id, group_title) |
outbound_setting | object | Outbound caller ID settings |
record_setting | object | Call recording settings |
voicemail_setting | object | Voicemail settings |
forwarding_setting | object | Call forwarding settings |
audio_setting | object | Audio file settings |
Initiate a Call
Make a POST request to start a click-to-call:
- cURL
- Python
- JavaScript
curl -X POST "https://app.sipsim.com/api/v2/calls" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone_id": 123,
"target_number": "+33698765432"
}'
import requests
access_token = "YOUR_ACCESS_TOKEN"
response = requests.post(
"https://app.sipsim.com/api/v2/calls",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
},
json={
"phone_id": 123,
"target_number": "+33698765432"
}
)
if response.status_code == 201:
call = response.json()["data"]
print(f"Call initiated: {call['id']}")
else:
error = response.json()
print(f"Error: {error['errors']}")
const accessToken = 'YOUR_ACCESS_TOKEN';
const response = await fetch('https://app.sipsim.com/api/v2/calls', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
phone_id: 123,
target_number: '+33698765432'
})
});
const data = await response.json();
if (response.status === 201) {
console.log('Call initiated:', data.data.id);
} else {
console.error('Error:', data.errors);
}
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone_id | integer | Yes | ID of the phone line to use |
target_number | string | Yes | Phone number to call (international format) |
Response
{
"status": 201,
"request_id": "...",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"id": "call_xyz789",
"phone_id": 123,
"target_number": "+33698765432",
"status": "initiating"
}
}
Error Handling
Phone Busy (409 Conflict)
If the phone already has an active call:
{
"status": 409,
"errors": ["Phone is currently on another call"],
"request_id": "...",
"timestamp": "2024-01-15T10:30:00Z",
"path": "/api/v2/calls"
}
How to handle:
import requests
import time
def initiate_call_with_retry(phone_id, target_number, max_retries=3):
"""Initiate a call with retry logic for busy phone."""
for attempt in range(max_retries):
response = requests.post(
"https://app.sipsim.com/api/v2/calls",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
},
json={
"phone_id": phone_id,
"target_number": target_number
}
)
if response.status_code == 201:
return response.json()["data"]
if response.status_code == 409:
# Phone busy, wait and retry
wait_time = 30 * (attempt + 1) # 30s, 60s, 90s
print(f"Phone busy, retrying in {wait_time}s...")
time.sleep(wait_time)
continue
# Other error, don't retry
response.raise_for_status()
raise Exception("Max retries exceeded - phone still busy")
Invalid Phone Number
If the target number is invalid:
{
"status": 400,
"errors": ["target_number is invalid"],
"request_id": "...",
"timestamp": "2024-01-15T10:30:00Z",
"path": "/api/v2/calls"
}
Phone Number Format
Always use international format (E.164) for phone numbers:
- Correct:
+33612345678 - Incorrect:
0612345678,06 12 34 56 78
CRM Integration Example
A common use case is adding click-to-call buttons in your CRM:
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
access_token = "YOUR_ACCESS_TOKEN"
@app.route("/api/click-to-call", methods=["POST"])
def click_to_call():
"""Handle click-to-call from CRM interface."""
data = request.json
phone_id = data["phone_id"] # ID of the SIPSIM phone to use
target_number = data["target_number"]
# Initiate the call
call_response = requests.post(
"https://app.sipsim.com/api/v2/calls",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
},
json={
"phone_id": phone_id,
"target_number": target_number
}
)
if call_response.status_code == 201:
call = call_response.json()["data"]
return jsonify({
"success": True,
"call_id": call["id"],
"message": "Call initiated - your phone will ring shortly"
})
else:
error = call_response.json()
return jsonify({
"success": False,
"error": error["errors"][0]
}), call_response.status_code
Best Practices
- Validate phone numbers before sending to the API
- Show feedback to users — Let them know the call is being initiated
- Handle the busy case gracefully — Inform users if the line is busy
- Log call IDs for troubleshooting and CRM integration
- Rate limit click-to-call buttons to prevent accidental double-clicks
Next Steps
- Working with Calls — Track and filter your calls
- AI Features — Get transcriptions of your calls
- Managing Tags — Tag calls for organization