Cancel Call
Cancel an active or scheduled call and terminate the LiveKit room
Cancel Call
Cancel an active or scheduled call. This endpoint terminates the LiveKit room if the call is in progress and updates the call status to cancelled.
Endpoint
DELETE /api/v1/calls/{callId}Authentication
Requires API key in request headers:
x-api-key: YOUR_API_KEYPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
callId | string | Yes | Unique call identifier |
Cancellable Call Statuses
This endpoint only works for calls with the following statuses:
initiated- Call has been created but not yet startedconnecting- Call is being establishedringing- Phone is ringingactive- Call is currently in progress
Note: Calls with status completed, failed, cancelled, no_answer, or busy cannot be cancelled.
Response
Returns the updated call status after cancellation.
Response Schema
{
"data": {
"id": "string",
"status": "cancelled"
},
"message": "Call cancelled successfully"
}Examples
Basic Request
curl -X DELETE "https://v2.heppu.ai/api/v1/calls/call_abc123" \
-H "x-api-key: YOUR_API_KEY"const callId = 'call_abc123';
const response = await fetch(`https://v2.heppu.ai/api/v1/calls/${callId}`, {
method: 'DELETE',
headers: {
'x-api-key': 'YOUR_API_KEY'
}
});
const data = await response.json();
console.log('Call cancelled:', data.data);import requests
call_id = 'call_abc123'
response = requests.delete(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
data = response.json()
print(f"Call cancelled: {data['data']}")Cancel with Error Handling
curl -X DELETE "https://v2.heppu.ai/api/v1/calls/call_abc123" \
-H "x-api-key: YOUR_API_KEY" \
-w "\nHTTP Status: %{http_code}\n"async function cancelCall(callId) {
try {
const response = await fetch(`https://v2.heppu.ai/api/v1/calls/${callId}`, {
method: 'DELETE',
headers: { 'x-api-key': 'YOUR_API_KEY' }
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
const data = await response.json();
console.log('✓ Call cancelled successfully');
return data.data;
} catch (error) {
console.error('✗ Failed to cancel call:', error.message);
throw error;
}
}
// Usage
await cancelCall('call_abc123');def cancel_call(call_id):
try:
response = requests.delete(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
response.raise_for_status()
data = response.json()
print('✓ Call cancelled successfully')
return data['data']
except requests.exceptions.HTTPError as e:
error_data = e.response.json()
print(f"✗ Failed to cancel call: {error_data['error']['message']}")
raise
# Usage
cancel_call('call_abc123')Check Status Before Cancelling
# First check status
STATUS=$(curl -s "https://v2.heppu.ai/api/v1/calls/call_abc123" \
-H "x-api-key: YOUR_API_KEY" | jq -r '.data.status')
# Cancel if active
if [[ "$STATUS" == "active" || "$STATUS" == "ringing" ]]; then
curl -X DELETE "https://v2.heppu.ai/api/v1/calls/call_abc123" \
-H "x-api-key: YOUR_API_KEY"
else
echo "Call cannot be cancelled (status: $STATUS)"
fiasync function safeCancelCall(callId) {
// First, get call details
const getResponse = await fetch(`https://v2.heppu.ai/api/v1/calls/${callId}`, {
headers: { 'x-api-key': 'YOUR_API_KEY' }
});
const { data: call } = await getResponse.json();
// Check if call can be cancelled
const cancellableStatuses = ['initiated', 'connecting', 'ringing', 'active'];
if (!cancellableStatuses.includes(call.status)) {
console.log(`Call cannot be cancelled (status: ${call.status})`);
return { cancelled: false, reason: `Call status is ${call.status}` };
}
// Cancel the call
const deleteResponse = await fetch(`https://v2.heppu.ai/api/v1/calls/${callId}`, {
method: 'DELETE',
headers: { 'x-api-key': 'YOUR_API_KEY' }
});
const result = await deleteResponse.json();
console.log('✓ Call cancelled successfully');
return { cancelled: true, data: result.data };
}def safe_cancel_call(call_id):
# First, get call details
get_response = requests.get(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
call = get_response.json()['data']
# Check if call can be cancelled
cancellable_statuses = ['initiated', 'connecting', 'ringing', 'active']
if call['status'] not in cancellable_statuses:
print(f"Call cannot be cancelled (status: {call['status']})")
return {'cancelled': False, 'reason': f"Call status is {call['status']}"}
# Cancel the call
delete_response = requests.delete(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
delete_response.raise_for_status()
result = delete_response.json()
print('✓ Call cancelled successfully')
return {'cancelled': True, 'data': result['data']}Response Examples
Success Response
{
"data": {
"id": "call_abc123",
"status": "cancelled"
},
"message": "Call cancelled successfully"
}Error Responses
Call Not Found
{
"error": {
"message": "Call not found",
"code": 404
}
}Call Already Completed
{
"error": {
"message": "Cannot cancel call with status: completed",
"code": 400
}
}Unauthorized
{
"error": {
"message": "Unauthorized",
"code": 401
}
}Access Denied
{
"error": {
"message": "Access denied - call belongs to different organization",
"code": 403
}
}Use Cases
Emergency Call Stop
Cancel all active calls for an agent in case of emergency:
async function emergencyStopAllCalls(agentId) {
// Get all active calls for the agent
const response = await fetch(
`https://v2.heppu.ai/api/v1/calls?agentId=${agentId}&status=active`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { data } = await response.json();
const activeCalls = data.calls;
console.log(`Found ${activeCalls.length} active calls to cancel`);
// Cancel each call
const results = await Promise.allSettled(
activeCalls.map(call =>
fetch(`https://v2.heppu.ai/api/v1/calls/${call.id}`, {
method: 'DELETE',
headers: { 'x-api-key': 'YOUR_API_KEY' }
})
)
);
const cancelled = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;
console.log(`Cancelled: ${cancelled}, Failed: ${failed}`);
return { cancelled, failed, total: activeCalls.length };
}Cancel Scheduled Call
Cancel a scheduled call before it starts:
def cancel_scheduled_call(call_id):
"""Cancel a scheduled call before it starts"""
# Get call details
response = requests.get(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
call = response.json()['data']
# Check if it's scheduled
if call['status'] != 'initiated':
print(f"Call is not scheduled (status: {call['status']})")
return False
# Cancel the call
cancel_response = requests.delete(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'}
)
if cancel_response.status_code == 200:
print(f"Scheduled call {call_id} cancelled successfully")
return True
return FalseBulk Cancel with Filtering
Cancel multiple calls based on criteria:
async function cancelCallsByCriteria(criteria) {
const { agentId, status, beforeDate } = criteria;
// Build query parameters
const params = new URLSearchParams();
if (agentId) params.append('agentId', agentId);
if (status) params.append('status', status);
if (beforeDate) params.append('endDate', beforeDate);
// Get matching calls
const response = await fetch(
`https://v2.heppu.ai/api/v1/calls?${params}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { data } = await response.json();
const calls = data.calls;
console.log(`Found ${calls.length} calls matching criteria`);
// Filter only cancellable calls
const cancellableStatuses = ['initiated', 'connecting', 'ringing', 'active'];
const cancellableCalls = calls.filter(call =>
cancellableStatuses.includes(call.status)
);
console.log(`${cancellableCalls.length} calls can be cancelled`);
// Cancel each call
const results = [];
for (const call of cancellableCalls) {
try {
const cancelResponse = await fetch(
`https://v2.heppu.ai/api/v1/calls/${call.id}`,
{
method: 'DELETE',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
if (cancelResponse.ok) {
results.push({ id: call.id, success: true });
} else {
const error = await cancelResponse.json();
results.push({ id: call.id, success: false, error: error.error.message });
}
} catch (error) {
results.push({ id: call.id, success: false, error: error.message });
}
}
return {
total: calls.length,
cancellable: cancellableCalls.length,
cancelled: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
details: results
};
}
// Usage
const result = await cancelCallsByCriteria({
agentId: '550e8400-e29b-41d4-a716-446655440000',
status: 'initiated'
});
console.log('Cancellation results:', result);Cancel with Retry Logic
Implement retry logic for cancellation:
import time
def cancel_call_with_retry(call_id, max_retries=3, delay=1):
"""Cancel a call with retry logic"""
for attempt in range(max_retries):
try:
response = requests.delete(
f'https://v2.heppu.ai/api/v1/calls/{call_id}',
headers={'x-api-key': 'YOUR_API_KEY'},
timeout=5
)
if response.status_code == 200:
print(f"✓ Call cancelled successfully on attempt {attempt + 1}")
return response.json()['data']
elif response.status_code == 400:
# Call cannot be cancelled (wrong status)
error = response.json()
print(f"✗ Call cannot be cancelled: {error['error']['message']}")
return None
elif response.status_code == 404:
# Call not found
print(f"✗ Call not found: {call_id}")
return None
except requests.exceptions.Timeout:
print(f"⚠ Timeout on attempt {attempt + 1}")
except requests.exceptions.RequestException as e:
print(f"⚠ Error on attempt {attempt + 1}: {str(e)}")
# Wait before retrying (except on last attempt)
if attempt < max_retries - 1:
time.sleep(delay)
print(f"✗ Failed to cancel call after {max_retries} attempts")
return NoneBest Practices
- Check status first - Verify call status before attempting to cancel
- Handle errors gracefully - Not all calls can be cancelled
- Use timeouts - Set reasonable timeouts for cancellation requests
- Implement retries - Network issues may occur during cancellation
- Log cancellations - Keep track of cancelled calls for auditing
- Batch operations - When cancelling multiple calls, use appropriate rate limiting
LiveKit Room Termination
When a call is cancelled:
- The LiveKit room is immediately deleted (if it exists)
- All participants are disconnected
- The call status is updated to
cancelled - The
endedAttimestamp is set to the current time
Note: If LiveKit room deletion fails, the call status will still be updated to cancelled, but a warning will be logged.
Related Endpoints
- Get Call Details - Check call status before cancelling
- List Calls - Find calls to cancel
- Schedule Call - Schedule a new call