ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Calls

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

MethodPathDescription
GET/v1/simulationsList simulation runs
POST/v1/simulationsStart a single simulation run
GET/v1/simulations/batchesList batches
POST/v1/simulations/batchesStart 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"
  }
}
FieldTypeDescription
idintegerSimulation run id
target_typestringagent or phone_number — what the test caller exercises
target_idinteger | nullId of the target agent / phone number
target_namestringName of the target (agent name or phone number)
agent_namestring | nullThe voice agent's name, resolved even for phone_number targets
directionstringoutbound (test caller places the call) or inbound (test caller answers)
modestringHow the run was wired internally: bot (bot-to-bot) or sip (SIP loopback). Read-only — not settable on create
target_numberstringOptional E.164 override for the remote side; empty uses the platform default
scenario_promptstringFree-form role-play prompt driving the test caller
statusstringqueued, in_progress, completed, failed
charge_multipliernumberCompatibility field. Current runs use 1; pricing is itemized per bot and telephony leg
estimated_cost_centsinteger | nullPre-call estimate for both sides, in USD cents
call_idinteger | nullThe call log the run produced
error_messagestringPopulated on status="failed"
metadataobjectInternal diagnostics (e.g. start errors)
requested_by_idinteger | nullUser who started the run (null for API-key runs)
started_at, completed_attimestamp | nullRun lifecycle
duration_secondsinteger | nullDerived from the underlying call
recording_urlstring | nullShort-lived playable recording URL
transcriptarraySimplified turns: {role, content, timestamp}
gradeobject | nullLatest grade of the underlying call, or null if ungraded

List runs

cURL
curl 'https://api.thunderphone.com/v1/simulations?limit=100' \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"

Query parameters

ParamTypeDescription
limitintegerDefault 100, max 500
offsetinteger
target_typestringFilter by agent or phone_number
target_idintegerFilter by target id (combine with target_type)
statusstringFilter 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
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
  }'
Python
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

FieldTypeRequiredDescription
target_typestringyesagent or phone_number
target_idintegeryesId of the target in this org
directionstringnooutbound (default) or inbound
target_numberstringnoE.164 for the remote side — defaults to the platform test number
scenario_promptstringno≤ 4000 chars. Drives the test caller
language / primary_languagestringnoLanguage for the test caller (ISO 639 or BCP-47; normalized server-side; unsupported codes are rejected)
simulator_productstringnoTurn 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_chargebooleanyesMust 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:

StatusCondition
400Validation failure (e.g. consent_to_charge missing / false)
402Insufficient balance
404Target agent or phone number not found
429More than 10 simulation runs already queued/in_progress in the org
502The 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]
}
FieldTypeDescription
idintegerBatch id
namestringOptional label
scenario_promptsarray of stringPrompts rotated across runs
matrix_dimensionsobject | nullSet when the batch was produced by a scenario matrix execution — records which condition axes were expanded
run_count, stagger_secondsintegerAs requested
statusstringBatch status
total_runs, completed_runs, failed_runsintegerProgress counters
estimated_minutesinteger | nullPre-flight duration estimate
estimated_cost_centsinteger | nullPre-flight cost estimate
run_idsarray of integerChild 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
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
  }'
FieldTypeRequiredDescription
target_typestringyesagent or phone_number
target_idintegeryes
directionstringnooutbound (default) or inbound
run_countintegerno1–20, default 3
stagger_secondsintegernoDelay between spawning successive runs (0–60, default 5)
scenario_promptsarray of stringnoUp to 20 prompts (≤ 4000 chars each), rotated per run (modulo)
target_numberstringno
namestringno≤ 255 chars
language / primary_languagestringnoLanguage for the test callers
consent_to_chargebooleanyesMust be true

Returns 201 Created with the batch object. Errors match single-run errors (400/402/404).

Get batch detail

cURL
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}.