Simulations
Run one-shot or batched AI caller simulations against an agent or a phone number, follow their progress, and inspect graded transcripts when they finish.
Simulations let you programmatically validate an agent: an AI test
caller role-plays a scenario against your agent, and the run captures
the resulting call, transcript, and grade. Each run produces a real
call log. Billing itemizes the selected agent,
the simulated caller, and any telephony leg; charge_multiplier is retained
as a response field for compatibility and is 1 for current runs. Use
runs for one-shot tests and batches
to execute several scenarios (or repetitions) with staggered starts.
For scripted, repeatable regression testing — saved scenarios, AI scenario generation, condition matrices, and release-gate suites — see Test scenarios & suites.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/simulations | List simulation runs |
POST | /v1/simulations | Start a single simulation run |
GET | /v1/simulations/batches | List batches |
POST | /v1/simulations/batches | Start a batch |
GET | /v1/simulations/batches/{batch_id} | Retrieve batch status and child run ids |
Simulation run object
{
"id": 7110,
"target_type": "agent",
"target_id": 12,
"target_name": "Customer Support Agent",
"agent_name": "Customer Support Agent",
"direction": "outbound",
"mode": "bot",
"target_number": "",
"scenario_prompt": "Roleplay as a customer asking about refunds.",
"status": "completed",
"charge_multiplier": 1,
"estimated_cost_cents": 16,
"call_id": 987654321,
"error_message": "",
"metadata": {},
"requested_by_id": 7,
"started_at": "2026-04-20T18:24:10.113Z",
"completed_at": "2026-04-20T18:25:04.822Z",
"created_at": "2026-04-20T18:24:09.011Z",
"updated_at": "2026-04-20T18:25:04.822Z",
"duration_seconds": 54,
"recording_url": "https://storage.googleapis.com/…",
"transcript": [
{ "role": "user", "content": "Hi, I want a refund.", "timestamp": null }
],
"grade": {
"id": 88,
"status": "completed",
"score": 92,
"call_outcome": "success",
"outcome_reasoning": "…",
"summary": "…",
"rubric_breakdown": {},
"detected_issues": [],
"grader_model": "heuristic-v1",
"grader_version": "v1",
"graded_at": "2026-04-20T18:25:11.002Z"
}
}| Field | Type | Description |
|---|---|---|
id | integer | Simulation run id |
target_type | string | agent or phone_number — what the test caller exercises |
target_id | integer | null | Id of the target agent / phone number |
target_name | string | Name of the target (agent name or phone number) |
agent_name | string | null | The voice agent's name, resolved even for phone_number targets |
direction | string | outbound (test caller places the call) or inbound (test caller answers) |
mode | string | How the run was wired internally: bot (bot-to-bot) or sip (SIP loopback). Read-only — not settable on create |
target_number | string | Optional E.164 override for the remote side; empty uses the platform default |
scenario_prompt | string | Free-form role-play prompt driving the test caller |
status | string | queued, in_progress, completed, failed |
charge_multiplier | number | Compatibility field. Current runs use 1; pricing is itemized per bot and telephony leg |
estimated_cost_cents | integer | null | Pre-call estimate for both sides, in USD cents |
call_id | integer | null | The call log the run produced |
error_message | string | Populated on status="failed" |
metadata | object | Internal diagnostics (e.g. start errors) |
requested_by_id | integer | null | User who started the run (null for API-key runs) |
started_at, completed_at | timestamp | null | Run lifecycle |
duration_seconds | integer | null | Derived from the underlying call |
recording_url | string | null | Short-lived playable recording URL |
transcript | array | Simplified turns: {role, content, timestamp} |
grade | object | null | Latest grade of the underlying call, or null if ungraded |
List runs
curl 'https://api.thunderphone.com/v1/simulations?limit=100' \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Query parameters
| Param | Type | Description |
|---|---|---|
limit | integer | Default 100, max 500 |
offset | integer | |
target_type | string | Filter by agent or phone_number |
target_id | integer | Filter by target id (combine with target_type) |
status | string | Filter by status |
Returns a plain array of simulation run objects,
newest first. Listing also self-heals runs whose completion signal was
lost (stale queued/in_progress runs are marked failed).
Start a single run
curl -X POST https://api.thunderphone.com/v1/simulations \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target_type": "agent",
"target_id": 12,
"direction": "outbound",
"scenario_prompt": "Roleplay as a customer asking about refunds.",
"consent_to_charge": true
}'run = requests.post(
"https://api.thunderphone.com/v1/simulations",
headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
json={
"target_type": "agent",
"target_id": 12,
"direction": "outbound",
"scenario_prompt": "Roleplay as a customer asking about refunds.",
"consent_to_charge": True,
},
).json()Request fields
| Field | Type | Required | Description |
|---|---|---|---|
target_type | string | yes | agent or phone_number |
target_id | integer | yes | Id of the target in this org |
direction | string | no | outbound (default) or inbound |
target_number | string | no | E.164 for the remote side — defaults to the platform test number |
scenario_prompt | string | no | ≤ 4000 chars. Drives the test caller |
language / primary_language | string | no | Language for the test caller (ISO 639 or BCP-47; normalized server-side; unsupported codes are rejected) |
simulator_product | string | no | Turn engine for the simulated caller: testing (default, bot-vs-bot guards) or spark (behaves like a responsive human — useful for warm-transfer consult testing) |
consent_to_charge | boolean | yes | Must be true — required to prevent accidental charges |
Returns 201 Created with the run object.
Status starts at queued and advances to completed or failed;
poll the list endpoint or
GET /v1/calls/{call_id} once
call_id is set.
Errors:
| Status | Condition |
|---|---|
400 | Validation failure (e.g. consent_to_charge missing / false) |
402 | Insufficient balance |
404 | Target agent or phone number not found |
429 | More than 10 simulation runs already queued/in_progress in the org |
502 | The run could not be started (run is marked failed with an error_message) |
Batches
A batch executes N staggered runs against the same target. The
response returns immediately with a batch in in_progress; poll the
detail endpoint for progress and per-run results.
Batch object
{
"id": 201,
"name": "",
"target_type": "agent",
"target_id": 12,
"direction": "outbound",
"target_number": "",
"scenario_prompts": ["Ask about refunds.", "…"],
"matrix_dimensions": null,
"run_count": 5,
"stagger_seconds": 2,
"status": "in_progress",
"total_runs": 5,
"completed_runs": 0,
"failed_runs": 0,
"estimated_minutes": 10,
"estimated_cost_cents": 80,
"error_message": "",
"requested_by_id": 7,
"started_at": "2026-04-20T18:24:09.011Z",
"completed_at": null,
"created_at": "2026-04-20T18:24:09.011Z",
"updated_at": "2026-04-20T18:24:09.011Z",
"run_ids": [7110, 7111, 7112, 7113, 7114]
}| Field | Type | Description |
|---|---|---|
id | integer | Batch id |
name | string | Optional label |
scenario_prompts | array of string | Prompts rotated across runs |
matrix_dimensions | object | null | Set when the batch was produced by a scenario matrix execution — records which condition axes were expanded |
run_count, stagger_seconds | integer | As requested |
status | string | Batch status |
total_runs, completed_runs, failed_runs | integer | Progress counters |
estimated_minutes | integer | null | Pre-flight duration estimate |
estimated_cost_cents | integer | null | Pre-flight cost estimate |
run_ids | array of integer | Child run ids — fetch runs via GET /v1/simulations |
List batches
GET /v1/simulations/batches — query params status, limit
(default 100, max 500), offset. Returns a plain array of batch
objects, newest first.
Start a batch
curl -X POST https://api.thunderphone.com/v1/simulations/batches \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target_type": "agent",
"target_id": 12,
"direction": "outbound",
"run_count": 5,
"stagger_seconds": 2,
"scenario_prompts": [
"Ask about refunds.",
"Ask for hours of operation.",
"Complain about a delayed shipment.",
"Ask to speak with a human.",
"Ask an unrelated trivia question."
],
"consent_to_charge": true
}'| Field | Type | Required | Description |
|---|---|---|---|
target_type | string | yes | agent or phone_number |
target_id | integer | yes | |
direction | string | no | outbound (default) or inbound |
run_count | integer | no | 1–20, default 3 |
stagger_seconds | integer | no | Delay between spawning successive runs (0–60, default 5) |
scenario_prompts | array of string | no | Up to 20 prompts (≤ 4000 chars each), rotated per run (modulo) |
target_number | string | no | |
name | string | no | ≤ 255 chars |
language / primary_language | string | no | Language for the test callers |
consent_to_charge | boolean | yes | Must be true |
Returns 201 Created with the batch object. Errors
match single-run errors (400/402/404).
Get batch detail
curl https://api.thunderphone.com/v1/simulations/batches/201 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns the batch object, or 404. run_ids is a
list of child-run ids, not nested run objects — fetch each run via
GET /v1/simulations or follow the call_id on each
run to GET /v1/calls/{call_id}.
Related
Saved scenarios, AI scenario generation, condition matrices, and release-gate suites.
Build the agent being tested — simulations exercise the agent's draft config.
Inspect the resulting call logs and transcripts.
Simulation charges itemize the selected agent, simulated caller, and any telephony leg.