# Simulate a call through a flow

`POST https://api.morevoice.ai/v1/flows/{id}/simulate`

Runs a text-only call through the flow's draft (or a published `version`) with the real flow engine and the assistant's model, and answers what happened: the turns, the nodes entered, the actions (dry runs: no API or tool request is sent) and where the call stands. Send the caller's lines as `messages` / `message`, keys as `dtmf`, silence as `no_response`. Continue the same simulation with `sim_id` (it lives 15 minutes). With a test key the models are mocks.

Send `Accept: text/event-stream` to receive the same simulation as Server-Sent Events while it runs: `sim.started` (`sim_id`, `resumed`, `kind`), then `sim.node`, `sim.turn` and `sim.action` (each with the fields of the matching FlowSimulation entry) in order, and last `sim.done`, whose data is this endpoint's JSON answer; `error` ends a failed run. Closing the connection stops the simulation. A streamed answer counts against the key's stream limit and is not stored for Idempotency-Key replays.

Authenticate with a secret API key: `Authorization: Bearer $MOREVOICE_API_KEY`.

Operation ID: `flows_simulate`.

## Parameters

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | yes | A flow ID (`flow_…`). |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `MoreVoice-Version` | `string` | no | The API version to use for this request. Defaults to the version the API key is pinned to. |
| `Idempotency-Key` | `string` | no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |

## Request body

Optional, `application/json`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `dtmf` | `string` | no | Keys the caller presses, in order (after `messages`, before `message`); the entry then completes. |
| `message` | `string` | no | One more caller line (after `messages`). |
| `messages` | `string[]` | no | What the caller says, one line per turn, in order. |
| `no_response` | `boolean` | no | The caller stays silent (a no-response timeout). |
| `sim_id` | `string` | no | Continue this simulation (the `id` of an earlier answer; it lives 15 minutes after its last turn). Default: a new one. |
| `variables` | `Record<string, string \| number \| boolean \| null>` | no | Variables for the simulated call (merged over the flow's sample values). |
| `version` | `integer` | no | Simulate this published version. Default: the draft. |

Example:

```json
{
  "messages": [
    "Hi, I'd like to book an appointment",
    "Tomorrow at 10"
  ]
}
```

## Responses

### 200 OK

OK Returns `FlowSimulation`.

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"flow_simulation"` | — |
| `id` | `string` | The simulation's ID: send it as `sim_id` to continue. |
| `flow_id` | `string` | A flow ID (prefix `flow_`). |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `resumed` | `boolean` | This request continued an existing simulation. |
| `turns` | `object[]` | What was said during this request, in order. |
| `path` | `object[]` | The nodes entered during this request, in order. |
| `actions` | `object[]` | Actions and tool calls during this request (dry runs). |
| `ended` | `boolean` | — |
| `end_reason` | `string \| null` | — |
| `current_node_key` | `string \| null` | — |
| `variables` | `object` | The call's variables now (sensitive ones redacted). |
| `coverage` | `object` | — |
| `coverage.node_pct` | `number` | — |
| `coverage.transition_pct` | `number` | — |
| `coverage.visited_node_keys` | `string[]` | — |
| `coverage.unvisited_node_keys` | `string[]` | — |
| `coverage.node_count` | `integer` | — |
| `coverage.transition_count` | `integer` | — |

Example:

```json
{
  "actions": [],
  "coverage": {
    "node_count": 5,
    "node_pct": 60,
    "transition_count": 5,
    "transition_pct": 40,
    "unvisited_node_keys": [
      "bye"
    ],
    "visited_node_keys": [
      "start",
      "ask_id"
    ]
  },
  "current_node_key": "ask_id",
  "end_reason": null,
  "ended": false,
  "flow_id": "flow_7Kp1Ns4Vy6Ab9Dg2Hj5Lm8",
  "id": "sim_7Hk2pQ9xZb4Lm8Nc3Rt6Vw",
  "livemode": false,
  "object": "flow_simulation",
  "path": [
    {
      "from_node_key": null,
      "node_key": "start",
      "node_type": "start",
      "transition_key": null,
      "via": "start"
    },
    {
      "from_node_key": "start",
      "node_key": "ask_id",
      "node_type": "question",
      "transition_key": "t_next",
      "via": "always"
    }
  ],
  "resumed": false,
  "turns": [
    {
      "node_key": "start",
      "role": "assistant",
      "source": "script",
      "text": "Hello, you've reached Acme."
    },
    {
      "node_key": "ask_id",
      "role": "customer",
      "source": "speech",
      "text": "Hi, I'd like to book an appointment"
    },
    {
      "node_key": "ask_id",
      "role": "assistant",
      "source": "ai",
      "text": "Sure! What is your ID number?"
    }
  ],
  "variables": {
    "customer_id": null
  }
}
```

### 400 Bad Request

The request is invalid: a parameter is missing, malformed or unknown, or the version header is unknown. Returns `ErrorEnvelope`.

### 401 Unauthorized

No valid API key was sent. Returns `ErrorEnvelope`.

### 403 Forbidden

The key may not do this (a missing scope, a plan limit, or a compliance block). Returns `ErrorEnvelope`.

### 404 Not Found

No object with this ID exists in this organisation and mode. Returns `ErrorEnvelope`.

### 409 Conflict

The request conflicts with the object's state, or the Idempotency-Key was reused with other parameters. Returns `ErrorEnvelope`.

### 429 Too Many Requests

Too many requests, or no call capacity right now. Retry after the Retry-After delay. Returns `ErrorEnvelope`.

### 500 Internal Server Error

Something went wrong on MoreVoice's side. Retry with the same Idempotency-Key. Returns `ErrorEnvelope`.

## Code samples

**TypeScript**

```ts
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // MOREVOICE_API_KEY from the environment

const flowSimulation = await mv.flows.simulate("flow_7Hk2Lm9Qp", {
	messages: [
		"Hi, I'd like to book an appointment",
		"Tomorrow at 10",
	],
});
console.log(flowSimulation);
```

**Python**

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

flow_simulation = client.flows.simulate("flow_7Hk2Lm9Qp", {
    "messages": [
        "Hi, I'd like to book an appointment",
        "Tomorrow at 10",
    ],
})
print(flow_simulation)
```

**cURL**

```sh
curl -X POST https://api.morevoice.ai/v1/flows/flow_7Hk2Lm9Qp/simulate \
  -H "Authorization: Bearer $MOREVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "messages": [
    "Hi, I'\''d like to book an appointment",
    "Tomorrow at 10"
  ]
}'
```
