curl --request POST \
--url https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/ \
--header 'Content-Type: application/json' \
--header 'X-CEKURA-API-KEY: <api-key>' \
--data '
{
"spec": "<unknown>",
"file": "<string>",
"agent_id": 123,
"name": "<string>",
"frequency": 2,
"agent_number": "<string>",
"outbound_phone_number": "<string>",
"concurrency_limit": 2,
"mock_tool_names": [
"<string>"
],
"livekit_data": {},
"pipecat_data": {}
}
'import requests
url = "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/"
payload = {
"spec": "<unknown>",
"file": "<string>",
"agent_id": 123,
"name": "<string>",
"frequency": 2,
"agent_number": "<string>",
"outbound_phone_number": "<string>",
"concurrency_limit": 2,
"mock_tool_names": ["<string>"],
"livekit_data": {},
"pipecat_data": {}
}
headers = {
"X-CEKURA-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-CEKURA-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
spec: '<unknown>',
file: '<string>',
agent_id: 123,
name: '<string>',
frequency: 2,
agent_number: '<string>',
outbound_phone_number: '<string>',
concurrency_limit: 2,
mock_tool_names: ['<string>'],
livekit_data: {},
pipecat_data: {}
})
};
fetch('https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'spec' => '<unknown>',
'file' => '<string>',
'agent_id' => 123,
'name' => '<string>',
'frequency' => 2,
'agent_number' => '<string>',
'outbound_phone_number' => '<string>',
'concurrency_limit' => 2,
'mock_tool_names' => [
'<string>'
],
'livekit_data' => [
],
'pipecat_data' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-CEKURA-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/"
payload := strings.NewReader("{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-CEKURA-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/")
.header("X-CEKURA-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-CEKURA-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"agent": 123,
"status": "pending",
"run_as_text": false,
"runs": {
"id": 274,
"status": "pending",
"scenario": 1,
"number": null,
"inbound_number": "+11234567890",
"scenario_name": "Customer Support Call (Agent Inbound = True)",
"test_profile_data": null,
"outbound_dial_window_opens_at": null,
"outbound_dial_window_seconds": null,
"outbound_dial_window_closes_at": null
},
"created_at": "2025-02-25T21:00:01.990052Z"
}{
"field_name": [
"<string>"
]
}Run Scenarios From Spec JSON
Run agent test cases straight from a JSON spec, without creating evaluators. Keep the spec in your repository and post it from CI.
curl --request POST \
--url https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/ \
--header 'Content-Type: application/json' \
--header 'X-CEKURA-API-KEY: <api-key>' \
--data '
{
"spec": "<unknown>",
"file": "<string>",
"agent_id": 123,
"name": "<string>",
"frequency": 2,
"agent_number": "<string>",
"outbound_phone_number": "<string>",
"concurrency_limit": 2,
"mock_tool_names": [
"<string>"
],
"livekit_data": {},
"pipecat_data": {}
}
'import requests
url = "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/"
payload = {
"spec": "<unknown>",
"file": "<string>",
"agent_id": 123,
"name": "<string>",
"frequency": 2,
"agent_number": "<string>",
"outbound_phone_number": "<string>",
"concurrency_limit": 2,
"mock_tool_names": ["<string>"],
"livekit_data": {},
"pipecat_data": {}
}
headers = {
"X-CEKURA-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-CEKURA-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
spec: '<unknown>',
file: '<string>',
agent_id: 123,
name: '<string>',
frequency: 2,
agent_number: '<string>',
outbound_phone_number: '<string>',
concurrency_limit: 2,
mock_tool_names: ['<string>'],
livekit_data: {},
pipecat_data: {}
})
};
fetch('https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'spec' => '<unknown>',
'file' => '<string>',
'agent_id' => 123,
'name' => '<string>',
'frequency' => 2,
'agent_number' => '<string>',
'outbound_phone_number' => '<string>',
'concurrency_limit' => 2,
'mock_tool_names' => [
'<string>'
],
'livekit_data' => [
],
'pipecat_data' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-CEKURA-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/"
payload := strings.NewReader("{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-CEKURA-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/")
.header("X-CEKURA-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cekura.ai/test_framework/v1/scenarios/run_scenarios_json/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-CEKURA-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"spec\": \"<unknown>\",\n \"file\": \"<string>\",\n \"agent_id\": 123,\n \"name\": \"<string>\",\n \"frequency\": 2,\n \"agent_number\": \"<string>\",\n \"outbound_phone_number\": \"<string>\",\n \"concurrency_limit\": 2,\n \"mock_tool_names\": [\n \"<string>\"\n ],\n \"livekit_data\": {},\n \"pipecat_data\": {}\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"agent": 123,
"status": "pending",
"run_as_text": false,
"runs": {
"id": 274,
"status": "pending",
"scenario": 1,
"number": null,
"inbound_number": "+11234567890",
"scenario_name": "Customer Support Call (Agent Inbound = True)",
"test_profile_data": null,
"outbound_dial_window_opens_at": null,
"outbound_dial_window_seconds": null,
"outbound_dial_window_closes_at": null
},
"created_at": "2025-02-25T21:00:01.990052Z"
}{
"field_name": [
"<string>"
]
}Authorizations
API Key Authentication. It should be included in the header of each request.
Query Parameters
Validate and price the spec without running or charging anything.
Body
Accepts a spec as a JSON body or an uploaded file.
CI posts JSON directly; the dashboard uploads the same file the customer has
in their repo. Both land in spec.
The test-suite spec. Use this when posting JSON directly.
A .json spec file. Use this instead of 'spec' for uploads.
Agent to test. Overrides 'agent_id' in the spec, letting one spec file run against several environments.
Label for this run. Defaults to the spec's suite name.
255How to reach the agent: 'voice' for a phone call (the default), 'text' for chat, 'elevenlabs' for an ElevenLabs WebRTC session, or 'livekit_v2' or 'pipecat_v2' for their respective WebRTC sessions.
voice- voicetext- textelevenlabs- elevenlabslivekit_v2- livekit_v2pipecat_v2- pipecat_v2
voice, text, elevenlabs, livekit_v2, pipecat_v2 Run every test case this many times, on top of each case's own 'frequency' in the spec. Default 1.
x >= 1Voice and text only. The agent's phone number for this run, in E.164 format, overriding the agent's configured contact number.
Voice and text only. For an inbound voice agent, the number Cekura dials, in E.164 format; defaults to 'agent_number', then the agent's contact number. Rejected for outbound voice agents: set 'phone_number' per test case in the spec instead. For SMS and WhatsApp agents, the contact number for the conversation.
Voice, inbound agents only. 'same_number' (default) places every call from the same Cekura number; 'different_numbers' spreads them across the pool and cannot be combined with 'phone_number' in the spec.
same_number- same_numberdifferent_numbers- different_numbers
same_number, different_numbers Cap on the number of runs executed in parallel. Overrides the spec's 'defaults.concurrency_limit'.
x >= 1Names of mock tools to activate for this run. Omit (or send null) to mock every tool configured on the agent; send an empty list to mock none.
Optional LiveKit overrides. These apply only to this run and are not part of the portable test-suite spec.
Show child attributes
Show child attributes
Optional Pipecat Cloud overrides. These apply only to this run and are not part of the portable test-suite spec.
Show child attributes
Show child attributes
Response
ID of the result
ID of the agent
Status of the result
pending, running, completed, failed Whether the scenario ran as text or not
false
Show child attributes
Show child attributes
{
"id": 274,
"status": "pending",
"scenario": 1,
"number": null,
"inbound_number": "+11234567890",
"scenario_name": "Customer Support Call (Agent Inbound = True)",
"test_profile_data": null,
"outbound_dial_window_opens_at": null,
"outbound_dial_window_seconds": null,
"outbound_dial_window_closes_at": null
}
{
"id": 275,
"status": "pending",
"scenario": 2,
"number": "+11234567890",
"inbound_number": null,
"scenario_name": "Outbound Sales Call (Agent Inbound = False)",
"test_profile_data": null,
"outbound_dial_window_opens_at": "2026-06-24T10:47:30Z",
"outbound_dial_window_seconds": 300,
"outbound_dial_window_closes_at": "2026-06-24T10:52:30Z"
}
"2025-02-25T21:00:01.990052Z"