Skip to main content

Overview

Testing outbound calls involves two main steps:
  1. Getting the list of evaluators with phone numbers
  2. Running the selected evaluators for outbound testing
Phone number regional coverage: Cekura provides testing phone numbers in a limited set of regions. If you need a receiving number in a region Cekura doesn’t cover (for example, an Indian +91 number), you can import your own number from Plivo or Twilio. Imported numbers work identically to Cekura-provisioned numbers for inbound and outbound testing — your system calls the imported number and Cekura’s testing agent answers.

Prerequisites

  • Outbound mode enabled: Agent’s inbound must be set to false
  • Agent contact_number set to your outbound caller ID: Cekura validates the caller ID (ANI) of every incoming outbound-test call against the agent’s contact_number field. If your agent dials from +15551234567, the agent’s contact_number must be exactly +15551234567 in E.164 format. Set this in Agent settings or via the Update Agent API.
  • Evaluators ready: Created evaluators with valid phone numbers
Caller ID mismatch causes silent rejects. If the contact_number on your agent is empty or doesn’t match the number your agent dials from, Cekura drops the incoming call without returning a SIP error your telephony provider can surface. The symptom on the provider side (Twilio, Telnyx, etc.) is Busy, NoAnswer, or a generic failed dial — no audio, no answer, no descriptive error. Confirm contact_number matches your actual outbound caller ID before debugging anything further downstream.

Getting Evaluators and Phone Numbers

To retrieve the available evaluators and their associated phone numbers:
  1. Use the List Evaluators API endpoint
  2. The response will include both evaluators and their corresponding phone numbers for testing

API Reference

Check out the List Evaluators API documentation

Example Code Running Outbound Call

import requests
import time
from typing import Callable, Dict, List

# Set variables
API_KEY = "api_key" # change_me
SCENARIO_IDS = [1514, 1515] # change_me
AGENT_ID = 26 # change_me


def call(number: str, test_profile: Dict | None) -> None:
    '''
    Outbound call function to be implemented
    Example: number = "+14154133900"
    '''
    print(f"📞 Initiating outbound call to {number}...")  # Replace with actual call logic


headers = {
    'X-CEKURA-API-KEY': API_KEY,
    'Content-Type': 'application/json'
}

base_url = "https://api.cekura.ai/test_framework"


def check_agent_inbound(agent_id: int) -> bool:
    url = f'{base_url}/v1/aiagents/{agent_id}/'
    data = {
        'agent_id': agent_id
    }
    response = requests.get(url, headers=headers, json=data)

    if response.status_code == 200:
        return response.json()["inbound"]
    elif response.status_code == 404:
        print(f"❌ Agent {agent_id} not found")
        exit(0)
    else:
        print(f"❌ Error checking agent inbound status: {response.status_code}")
        exit(0)

def run_evaluators(scenarios: List[int], agent_id: int, freq: int = 1) -> Dict:
    
    url = f'{base_url}/v1/scenarios/run_scenarios/'

    data = {
        'scenarios': scenarios,
        'freq': freq,
        'agent_id': agent_id
    }

    response = requests.post(url, headers=headers, json=data)

    return response.json()

status_message = {
    "completed": "✅ Run completed successfully",
    "failed": "❌ Run failed during execution",
    "pending": "⏳ Run is waiting to be executed",
    "in_progress": "🔄 Run is in progress",
    "evaluating": "🔍 Run is being evaluated",
    "in_queue": "📋 Run is waiting in queue",
    "timeout": "⏱️ Run timed out after exceeding time limit"
}

def monitor_runs(response_json: Dict, func: Callable, call_func: bool = False) -> None:

    run_ids = [str(run["id"]) for run in response_json.get("runs")]
    test_profiles = { run["id"]: run["test_profile_data"] for run in response_json.get("runs")}

    while run_ids:

        url = f'{base_url}/v1/runs/bulk/?run_ids={",".join(run_ids)}'

        runs_list = requests.get(url, headers=headers).json()

        for run in runs_list:
            phone_number = run.get("outbound_number")
            if run["status"] == "pending" and call_func:
                func(phone_number, test_profiles[run["id"]])  # Call function immediately
                run_ids.remove(str(run["id"]))
            
            if not call_func:
                print(f"{status_message.get(run['status'])} - Run ID: {run['id']}, Phone: {phone_number}")

            if run["status"] in ["completed", "failed", "timeout"]:
                run_ids.remove(str(run["id"]))
        
        if not run_ids:
            break

        time.sleep(3)  # Poll every 3 seconds


if check_agent_inbound(AGENT_ID):
    print(f"⚠️ Please change Agent {AGENT_ID} Inbound to False before running this script")
    exit(0)

response_json = run_evaluators(SCENARIO_IDS, AGENT_ID)
monitor_runs(response_json, func=call, call_func=True)
monitor_runs(response_json, func=call, call_func=False)

Example Response Structure

{
  "count": 20,
  "next": "https://api.cekura.ai/test_framework/v1/scenarios-external/?agent_id=4&page=2",
  "previous": null,
  "results": [
    {
      "id": 201,
      "name": "Hindi Elderly Telephonic Inquiry",
      "personality_name": "Hindi, Old, Hearing Issue",
      "phone_number": "+14159976447"
    }
    // Additional evaluators ...
  ]
}

Running Outbound Call Tests

Once you have your list of evaluators and phone numbers:
  1. Select which evaluators you want to run
  2. Execute the evaluators using the Running Evaluators API
  3. The system will automatically make outbound calls to the phone numbers associated with the selected evaluators
  4. Monitor the test results through the dashboard

API Reference

Learn more about the Running Evaluators API

Troubleshooting

Cekura rejects incoming outbound-test calls whose caller ID doesn’t match a configured contact_number on any agent in your project. Open the agent under test and confirm its contact_number is set to the exact E.164 number your agent dials from. An empty or mismatched contact_number causes every outbound attempt to be silently dropped — the symptom on your provider side is Busy / NoAnswer / no-pickup, with no audio and no SIP error explaining why. After setting the correct contact_number, trigger a new run; existing runs that hit this state will remain in timeout and cannot be recovered.
A timeout with duration: 0 and an empty transcript means the call never connected to Cekura. Work through these in order:
  1. Caller-ID match — see the entry above; this is the most common cause.
  2. Dial actually happened — check your telephony provider’s logs to confirm the outbound call was placed.
  3. Destination number — confirm the number your agent dialed matches the outbound_number returned for that run in the bulk runs API response.