Heppu AI
Calls API

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_KEY

Path Parameters

ParameterTypeRequiredDescription
callIdstringYesUnique call identifier

Cancellable Call Statuses

This endpoint only works for calls with the following statuses:

  • initiated - Call has been created but not yet started
  • connecting - Call is being established
  • ringing - Phone is ringing
  • active - 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)"
fi
async 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 False

Bulk 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 None

Best Practices

  1. Check status first - Verify call status before attempting to cancel
  2. Handle errors gracefully - Not all calls can be cancelled
  3. Use timeouts - Set reasonable timeouts for cancellation requests
  4. Implement retries - Network issues may occur during cancellation
  5. Log cancellations - Keep track of cancelled calls for auditing
  6. Batch operations - When cancelling multiple calls, use appropriate rate limiting

LiveKit Room Termination

When a call is cancelled:

  1. The LiveKit room is immediately deleted (if it exists)
  2. All participants are disconnected
  3. The call status is updated to cancelled
  4. The endedAt timestamp 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.

On this page