> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Simulations

> Run one-shot or batched AI simulations against an agent or phone number and inspect results.

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](/api-reference/calls) (billed at the run's
`charge_multiplier`). 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](/api-reference/test-scenarios).

## 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 |

<Note>
  **Deprecated aliases.** `/v1/test-calls` (list + create runs),
  `/v1/test-call-batches`, and `/v1/test-call-batches/{batch_id}`
  are older aliases for the same handlers. They keep working, but new
  integrations should use the canonical `/v1/simulations` routes.
</Note>

<a id="test-call-run-object" />

## Simulation run object

```json theme={null}
{
  "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": 2,
  "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            | Multiplier applied to normal call billing                                                                        |
| `estimated_cost_cents`       | integer \| null   | Pre-call estimate in USD cents                                                                                   |
| `call_id`                    | integer \| null   | The [call log](/api-reference/calls) 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](/api-reference/calls#ai-call-grading) of the underlying call, or `null` if ungraded               |

***

## List runs

<CodeGroup>
  ```bash cURL theme={null}
  curl 'https://api.thunderphone.com/v1/simulations?limit=100' \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```
</CodeGroup>

### 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](#simulation-run-object),
newest first. Listing also self-heals runs whose completion signal was
lost (stale `queued`/`in_progress` runs are marked `failed`).

***

## Start a single run

<CodeGroup>
  ```bash cURL theme={null}
  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 Python theme={null}
  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()
  ```
</CodeGroup>

### 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](#simulation-run-object).
Status starts at `queued` and advances to `completed` or `failed`;
poll the list endpoint or
[`GET /v1/calls/{call_id}`](/api-reference/calls#retrieve-a-call) 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

```json theme={null}
{
  "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](/api-reference/test-scenarios) — 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](#simulation-run-object) 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

<CodeGroup>
  ```bash cURL theme={null}
  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
    }'
  ```
</CodeGroup>

| 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](#batch-object). Errors
match [single-run errors](#start-a-single-run) (`400`/`402`/`404`).

### Get batch detail

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.thunderphone.com/v1/simulations/batches/201 \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```
</CodeGroup>

Returns the [batch object](#batch-object), or `404`. `run_ids` is a
list of child-run ids, not nested run objects — fetch each run via
[`GET /v1/simulations`](#list-runs) or follow the `call_id` on each
run to [`GET /v1/calls/{call_id}`](/api-reference/calls#retrieve-a-call).

***

## Related

<CardGroup cols={2}>
  <Card title="Test scenarios & suites" icon="list-check" href="/api-reference/test-scenarios">
    Saved scenarios, AI scenario generation, condition matrices, and release-gate suites.
  </Card>

  <Card title="Agents" icon="robot" href="/api-reference/agents">
    Build the agent being tested — simulations exercise the agent's draft config.
  </Card>

  <Card title="Calls" icon="phone" href="/api-reference/calls">
    Inspect the resulting call logs and transcripts.
  </Card>

  <Card title="Billing" icon="credit-card" href="/api-reference/billing">
    Simulation minutes bill at the run's charge multiplier.
  </Card>
</CardGroup>
