curl --request PATCH \
--url https://api.cekura.ai/test_framework/v1/scenarios/{id}/ \
--header 'Content-Type: application/json' \
--header 'X-CEKURA-API-KEY: <api-key>' \
--data '
{
"name": "Refund flow v2",
"instructions": "Ask for the order ID before refunding."
}
'import requests
url = "https://api.cekura.ai/test_framework/v1/scenarios/{id}/"
payload = {
"name": "Refund flow v2",
"instructions": "Ask for the order ID before refunding."
}
headers = {
"X-CEKURA-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-CEKURA-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Refund flow v2', instructions: 'Ask for the order ID before refunding.'})
};
fetch('https://api.cekura.ai/test_framework/v1/scenarios/{id}/', 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/{id}/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Refund flow v2',
'instructions' => 'Ask for the order ID before refunding.'
]),
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/{id}/"
payload := strings.NewReader("{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.cekura.ai/test_framework/v1/scenarios/{id}/")
.header("X-CEKURA-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cekura.ai/test_framework/v1/scenarios/{id}/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-CEKURA-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"agent": 123,
"project": 123,
"personality": 123,
"personality_name": "<string>",
"agent_name": "<string>",
"tags": [
"<string>"
],
"tool_ids": "<unknown>",
"metrics": [
123
],
"metric_names": [
"<string>"
],
"phone_number": "<string>",
"outbound_phone_number_data": {
"number": "<string>",
"phone_number_id": "<string>",
"id": 123
},
"first_message": "<string>",
"inbound_phone_number": 123,
"inbound_phone_number_data": {
"number": "<string>",
"phone_number_id": "<string>",
"id": 123
},
"instructions": "<string>",
"condition_audio": {},
"simulation_description": "<string>",
"information_fields": {},
"expected_outcome_prompt": "<string>",
"scenario_language": "af",
"scenario_type": "<string>",
"suite_type": "undefined",
"is_predefined": true,
"test_profile": 123,
"test_profile_data": {
"name": "<string>",
"id": 123,
"project": 123,
"agent": 123,
"agent_name": "<string>",
"information": "<unknown>",
"created_by": 123,
"last_updated_by": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"dynamic_variable_values": "<unknown>",
"generated_mock_tool_entries": "<unknown>",
"folder_path": "<string>",
"max_duration": 123
}{
"field_name": [
"<string>"
]
}Update Evaluator
Partially update an evaluator. Same field shape as create — see scenarios_create for details.
curl --request PATCH \
--url https://api.cekura.ai/test_framework/v1/scenarios/{id}/ \
--header 'Content-Type: application/json' \
--header 'X-CEKURA-API-KEY: <api-key>' \
--data '
{
"name": "Refund flow v2",
"instructions": "Ask for the order ID before refunding."
}
'import requests
url = "https://api.cekura.ai/test_framework/v1/scenarios/{id}/"
payload = {
"name": "Refund flow v2",
"instructions": "Ask for the order ID before refunding."
}
headers = {
"X-CEKURA-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {'X-CEKURA-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Refund flow v2', instructions: 'Ask for the order ID before refunding.'})
};
fetch('https://api.cekura.ai/test_framework/v1/scenarios/{id}/', 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/{id}/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Refund flow v2',
'instructions' => 'Ask for the order ID before refunding.'
]),
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/{id}/"
payload := strings.NewReader("{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.cekura.ai/test_framework/v1/scenarios/{id}/")
.header("X-CEKURA-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cekura.ai/test_framework/v1/scenarios/{id}/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-CEKURA-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Refund flow v2\",\n \"instructions\": \"Ask for the order ID before refunding.\"\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"name": "<string>",
"agent": 123,
"project": 123,
"personality": 123,
"personality_name": "<string>",
"agent_name": "<string>",
"tags": [
"<string>"
],
"tool_ids": "<unknown>",
"metrics": [
123
],
"metric_names": [
"<string>"
],
"phone_number": "<string>",
"outbound_phone_number_data": {
"number": "<string>",
"phone_number_id": "<string>",
"id": 123
},
"first_message": "<string>",
"inbound_phone_number": 123,
"inbound_phone_number_data": {
"number": "<string>",
"phone_number_id": "<string>",
"id": 123
},
"instructions": "<string>",
"condition_audio": {},
"simulation_description": "<string>",
"information_fields": {},
"expected_outcome_prompt": "<string>",
"scenario_language": "af",
"scenario_type": "<string>",
"suite_type": "undefined",
"is_predefined": true,
"test_profile": 123,
"test_profile_data": {
"name": "<string>",
"id": 123,
"project": 123,
"agent": 123,
"agent_name": "<string>",
"information": "<unknown>",
"created_by": 123,
"last_updated_by": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"dynamic_variable_values": "<unknown>",
"generated_mock_tool_entries": "<unknown>",
"folder_path": "<string>",
"max_duration": 123
}{
"field_name": [
"<string>"
]
}Authorizations
API Key Authentication. It should be included in the header of each request.
Path Parameters
A unique integer value identifying this scenario.
Body
ID of the agent. Either agent or project must be provided.
ID of the project. Either agent or project must be provided.
Alternative to agent ID - the assistant ID to use for this scenario
Example: "asst_1234567890"
Name of the evaluator
The personality of the evaluator
Type of scenario to create:
instruction: Standard instruction-based scenario where the instructions field contains plain textconditional_actions: Conditional action-based scenario where the instructions field contains a JSON structure
Default: instruction. Must be sent as conditional_actions when the request includes the
conditional_actions field.
instruction- Instructionconditional_actions- Conditional Actions
instruction, conditional_actions Scenario Instructions - the format depends on the scenario_type:
For scenario_type="instruction":
Provide plain text instructions that describe the scenario behavior.
Example: "You are a customer calling to inquire about your order status. Be polite and provide order number 12345 when asked."
For scenario_type="conditional_actions":
Use the structured conditional_actions field instead. If you send via instructions, pass a JSON-formatted string with the following structure:
{ "role": "A friendly customer calling about an appointment", "first_message": "Hi, I'd like to check on my upcoming appointment <silence time=\"1.5s\" />", "conditions": [ {"when": "The agent asks for your name", "say": "My name is Sarah Johnson"}, {"when": "The agent asks for your date of birth", "say": "January first, nineteen ninety"}, { "when": "The agent confirms your identity and provides appointment details", "say": "Thank you, that's all I needed <silence time=\"1.0s\" />", "then": ["<endcall />"] } ] }
Note: When sent via instructions, the value must be a JSON-formatted string (e.g. JSON.stringify(...) / json.dumps(...)). For new integrations, use the structured conditional_actions field below. Responses return instructions in this same shape.
Structure Details:
-
role(string): Personality/role description for the scenario actor -
first_message(string | object): What the scenario actor says first, always verbatim. Empty means wait for the agent to speak. Use{"say": "...", "then": [...]}to add follow-up steps after it. -
conditions(array): Ordered list of conditions that define the conversation flowEach condition object has:
-
when(string): Natural-language description of the main agent's observable action, written from a third-person observer perspective (e.g."The agent asks for your date of birth","The agent confirms the cancellation"). Matched semantically against the conversation context. -
say(string): What the scenario actor says or does, spoken verbatim. Wrap the whole step in<ai_generated>...</ai_generated>to have the actor improvise from it instead.- SSML pause tags:
<silence time="1.5s" />(interruptible) or<hold time="2s" />(not interruptible).<silence>lets the main agent jump in mid-pause;<hold>enforces dead air. - Emotion markers:
[laughter],[sigh], etc. (can repeat like[laughter] [laughter]) - Special actions:
<endcall />to end the call - Action tags only work in verbatim steps, not inside
<ai_generated>.
- SSML pause tags:
-
then(array of strings, optional): Follow-up steps in order. Each fires on the actor's next turn after one main-agent reply, regardless of its content.<interruption>is only allowed here.
-
Usage Tips:
- Write
whenas third-person descriptions of what the main agent does (e.g."The agent confirms the cancellation") - End with an
<endcall />step for clean call termination - Combine SSML tags and emotion markers naturally:
"Great <silence time=\"1.5s\" /> [laughter] [laughter]"
Structured conditional-actions object for scenario_type="conditional_actions": {"role": str, "first_message": str | {"say": str, "then": [str]}, "conditions": [{"when": str, "say": str, "then": [str]}]}. Steps are spoken verbatim unless wrapped whole in <ai_generated>...</ai_generated>. Use either conditional_actions (structured) or instructions (plain text) — not both. Responses return this format unless conditional_actions_format=legacy is passed. The earlier id-based format (id, condition, action, type, fixed_message) is also accepted.
Show child attributes
Show child attributes
Expected Outcome Prompt
Example: "The user should be able to complete the order"
List of metric IDs to associate with this evaluator
Example: [123, 456, 789]
List of tags to associate with the evaluator
Example: ["tag1", "tag2", "tag3"]
List of tool IDs to use for evaluator
[ "TOOL_DTMF", "TOOL_END_CALL", "TOOL_END_CALL_ONLY_ON_TRANSFER" ]
The test profile ID to use for the evaluator
The phone number ID to use for the evaluator (works for both inbound and outbound)
(Deprecated) The inbound phone number ID to use for the evaluator. Use phone_number instead, which works for both inbound and outbound.
Dot-separated path of the folder to assign this evaluator to (e.g. "Sales.Inbound"). Folders must be created before use — they are not auto-created. Use scenarios_create_folder_create to create each level before assigning evaluators. Example: to use "Sales.Inbound", first create "Sales", then create "Inbound" inside it.
Expected mock tool calls for this evaluator. Each entry has shape {tool_id, tool_name, new_entry: {input, output, response?}}. Auto-generated during scenario generation when the agent has mock tools attached; can also be set or edited manually.
Opening line the simulated caller says to the agent. Defaults to "Hello".
Max call duration in seconds for this scenario. If not provided or set to null, the scenario will use the project's max_call_duration setting.
Valid range: 10-3600 seconds (10 seconds to 60 minutes)
Example: 600 (10 minutes)
Language of the evaluator. Must match the chosen personality's language — mismatches are rejected.
af- Afrikaansar- Arabicbn- Bengalibg- Bulgarianzh- Chinese Simplifiedcs- Czechda- Danishnl- Dutchen- Englishet- Estonianfi- Finnishfr- Frenchde- Germanel- Greekgu- Gujaratihi- Hindihe- Hebrewhu- Hungarianid- Indonesianit- Italianja- Japanesekn- Kannadakk- Kazakhko- Koreanms- Malayml- Malayalammr- Marathimulti- Multilingualno- Norwegianpl- Polishpa- Punjabipt- Portuguesero- Romanianru- Russiansk- Slovakes- Spanishsv- Swedishth- Thaitr- Turkishtl- Tagalogta- Tamilte- Teluguuk- Ukrainianuz- Uzbekvi- Vietnamese
af, ar, bn, bg, zh, cs, da, nl, en, et, fi, fr, de, el, gu, hi, he, hu, id, it, ja, kn, kk, ko, ms, ml, mr, multi, no, pl, pa, pt, ro, ru, sk, es, sv, th, tr, tl, ta, te, uk, uz, vi Response
Name of the evaluator
Agent ID
Example: 2142
ID of the personality
Name of the personality
Name of the agent associated with this scenario
Example: "Customer Support Agent"
List of tags of the evaluators
Example: ["tag1", "tag2", "tag3"]
List of tool IDs to associate with this scenario
Example: ["TOOL_DTMF", "TOOL_END_CALL"]
List of metric IDs
Example: [123, 456, 789]
List of metric names
Example: ["Metric 1", "Metric 2", "Metric 3"]
Phone number eg: +17776666333
Phone number used for outbound calls in this scenario
Example: "+1234567890"
Show child attributes
Show child attributes
First message of the evaluator
(Deprecated) Phone number used for inbound calls in this scenario. Use outbound_phone_number_data instead.
After CEK-6517, both phone_number and inbound_phone_number fields are synced, so outbound_phone_number_data contains the unified phone number for both inbound and outbound calls.
Example: {"id": 123, "number": "+1234567890", "phone_number_id": "abc123"}
Show child attributes
Show child attributes
Scenario Instructions - format depends on scenario_type:
- For
instructiontype: Plain text instructions - For
conditional_actionstype: JSON string withrole,first_messageand aconditionsarray. Each condition haswhen(a natural-language third-person description of the main agent's observable action — e.g."The agent asks for your date of birth", matched semantically),say(spoken verbatim, with optional<silence time="1.5s" />(interruptible) or<hold time="2s" />(not interruptible) and[laughter]tags; wrap the whole step in<ai_generated>...</ai_generated>to improvise instead) and optionalthenfollow-up steps. Responses return this format unlessconditional_actions_format=legacyis passed. The earlier id-based format (id,condition,action,type,fixed_message) is still accepted on writes.
Pre-recorded clips available to this scenario's conditional actions, keyed by clip id. Read-only: upload and delete clips through condition-audio/. Reference a clip from any number of verbatim steps by pasting its tag; steps lists where it is used (e.g. conditions[1].then[0]).
Example:
{ "greeting": {"tag": "<audio id=\"greeting\"/>", "filename": "greeting.wav", "status": "ready", "duration": 2.4, "transcript": "Hi, is this the clinic?", "steps": ["first_message", "conditions[1]"]} }
Show child attributes
Show child attributes
Information fields of the evaluator Example:
{ "user_name": "John Doe", "user_email": "john.doe@example.com", }
Show child attributes
Show child attributes
Expected outcome prompt of the evaluator
Example: "The user should be able to complete the order"
Language of the scenario
af- Afrikaansar- Arabicbn- Bengalibg- Bulgarianzh- Chinese Simplifiedcs- Czechda- Danishnl- Dutchen- Englishet- Estonianfi- Finnishfr- Frenchde- Germanel- Greekgu- Gujaratihi- Hindihe- Hebrewhu- Hungarianid- Indonesianit- Italianja- Japanesekn- Kannadakk- Kazakhko- Koreanms- Malayml- Malayalammr- Marathimulti- Multilingualno- Norwegianpl- Polishpa- Punjabipt- Portuguesero- Romanianru- Russiansk- Slovakes- Spanishsv- Swedishth- Thaitr- Turkishtl- Tagalogta- Tamilte- Teluguuk- Ukrainianuz- Uzbekvi- Vietnamese
af, ar, bn, bg, zh, cs, da, nl, en, et, fi, fr, de, el, gu, hi, he, hu, id, it, ja, kn, kk, ko, ms, ml, mr, multi, no, pl, pa, pt, ro, ru, sk, es, sv, th, tr, tl, ta, te, uk, uz, vi Type of scenario:
instruction: Standard instruction-based scenario (plain text instructions)conditional_actions: Conditional action-based scenario (JSON with role, conditions, SSML tags, emotion markers)
Type of predefined suite (e.g., infrastructure)
undefined- Undefinedinfrastructure- Infrastructure
undefined, infrastructure Whether this is a predefined scenario (template)
Details of the test profile associated with this scenario. information uses the
sectioned shape: main_agent_variables are sent to the agent under test as
dynamic variables at call time; testing_agent_variables are persona/context
used by Cekura's simulated caller. Legacy flat dicts (no section keys) still
work — at call time they are delivered to both sides for backward compatibility.
Example:
{ "id": "<integer>", "agent_id": "<integer>", "name": "<string>", "information": { "main_agent_variables": { "user_id": "<string>", "account_id": "<string>" }, "testing_agent_variables": { "user_name": "<string>", "user_email": "<string>", "order_id": "<string>" } } }
Show child attributes
Show child attributes
REMOVED FROM API. Reads return the historical value for legacy rows (null for new rows). Writes are no longer accepted — supplying a non-null value yields a 400 error. Put dynamic-variable values in test_profile_data.information.main_agent_variables instead: create the test profile via POST /test-profiles/ and attach it via the test_profile field on the scenario.
Expected mock tool calls for this evaluator. Each entry has shape {tool_id, tool_name, new_entry: {input, output, response?}}. Auto-generated during scenario generation when the agent has mock tools attached; can also be set or edited manually.
Path of an existing folder to assign this evaluator to, e.g. "Sales" or "Sales.Inbound". Create it first with the create-folder endpoint.
Max call duration in seconds for this scenario. If not provided or set to null, the scenario will use the project's max_call_duration setting.
Valid range: 10-3600 seconds (10 seconds to 60 minutes)
Example: 600 (10 minutes)