> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.vapi.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.vapi.ai/_mcp/server.

# Edit a generated simulation suite

PATCH https://api.vapi.ai/eval/simulation/onboarding/suite/{suiteId}
Content-Type: application/json

Edits a generated suite: removes simulations, rewrites a simulation's scenario from your user's instruction, or regenerates the scenarios that failed or did not fit (`retryFailed`). Takes up to 2 minutes.

Send a new `patchKey` for each edit. Replaying an applied key changes nothing and returns the current suite, with `failedEditSimulationIds` listing the simulations that edit could not rewrite, so retry a timeout or a 5xx with the same key. A suite remembers its last 50 keys, so never reuse one.

Each org can make 60 edits per UTC day, shared with the dashboard. Removals and replays don't count. On a 429 `SIMULATION_ONBOARDING_QUOTA_EXCEEDED`, don't retry: the limit resets at 00:00 UTC.

Reference: https://docs.vapi.ai/api-reference/simulations/simulation-onboarding-controller-suite-update

## Authentication

- `Authorization` header (bearer token, required) — Authenticate server-side requests with a private Vapi API key. Create or copy a key from the [Vapi Dashboard](https://dashboard.vapi.ai) and send it in the `Authorization` header as `Bearer <token>`. Keep private API keys out of client-side code and public repositories.

## Request

### Path parameters

- `suiteId` (string, required)

### Body (application/json)

This endpoint expects a SimulationOnboardingSuiteUpdateDTO.

- `patchKey` (string, required) — A new key for each edit. Replaying an applied key changes nothing and returns the current suite. A suite remembers its last 50 keys, so never reuse one.
- `removeSimulationIds` (list of string, optional) — Simulations to take out of the suite. They are unlinked, not deleted.
- `edits` (list of SimulationOnboardingSuiteEditDTO, optional) — Simulations to rewrite in place, at most one edit per simulation: put every change for a simulation in one instruction.
- `retryFailed` (boolean, optional) — Regenerate the scenarios that failed or did not fit when the suite was generated.

## Response

### 200

- `suite` (SimulationOnboardingSuiteRefDTO, required) — The suite.
- `assistantId` (string, required) — Assistant the suite is assigned to.
- `simulations` (list of SimulationOnboardingSuiteSimulationDTO, required) — The suite's simulations, in order.
- `coverage` (SimulationOnboardingSuiteCoverageDTO, required) — Coverage of the agreed requirements.
- `requirements` (list of SimulationOnboardingRequirementDTO, required) — The agreed requirements the suite was generated from, to name the gaps.
- `docs` (SimulationOnboardingSuiteDocsDTO, required) — Documentation to link when you summarize the suite.
- `reused` (boolean, required) — True when the request matched one already applied (same `idempotencyKey` or `patchKey`) and nothing changed.
- `failedEditSimulationIds` (list of string, optional) — Simulations an edit asked to rewrite that were not rewritten, for example because the rewrite did not generate in time. Retry them with a new `patchKey`.
- `toolGaps` (list of SimulationOnboardingToolGapDTO, optional) — Tool paths the suite's simulations do not exercise as configured, such as a tool mocked although it runs natively. Omitted when there are none.

## Errors

### 400 Bad Request Error

The body is invalid, or the edited suite uses something the org's data-region policy doesn't allow, so it isn't saved.

- `any`

### 403 Forbidden Error

The key can't edit simulation suites.

- `any`

### 404 Not Found Error

The suite wasn't generated by `POST /eval/simulation/onboarding/suite` in this org, or the org doesn't have API access to these endpoints yet.

- `any`

### 422 Unprocessable Entity Error

An MCP server's tools could not be listed, so simulations would call them for real instead of a mock. Check the server is reachable and retry.

- `any`

### 429 Too Many Requests Error

`SIMULATION_ONBOARDING_QUOTA_EXCEEDED`: the org made 60 edits today. Don't retry; the limit resets at 00:00 UTC.

- `code` (enum, required) — Which refusal this is. Retry `SIMULATION_ONBOARDING_GENERATION_IN_PROGRESS` with the same key and `SIMULATION_ONBOARDING_CONCURRENCY_LIMITED` or `SIMULATION_ONBOARDING_QUOTA_UNAVAILABLE` after `retryAfterSeconds`, send a new key for `SIMULATION_ONBOARDING_IDEMPOTENCY_KEY_REUSED`, and stop for `SIMULATION_ONBOARDING_QUOTA_EXCEEDED`.
  - Allowed values: `SIMULATION_ONBOARDING_GENERATION_IN_PROGRESS`, `SIMULATION_ONBOARDING_IDEMPOTENCY_KEY_REUSED`, `SIMULATION_ONBOARDING_CONCURRENCY_LIMITED`, `SIMULATION_ONBOARDING_QUOTA_EXCEEDED`, `SIMULATION_ONBOARDING_QUOTA_UNAVAILABLE`
- `message` (string, required) — What happened, written for the user.
- `retryAfterSeconds` (double, optional) — Seconds to wait before retrying, the same value as the `Retry-After` header. For a daily limit, the seconds until 00:00 UTC.
- `limit` (double, optional) — The daily limit that was reached, on a daily-limit refusal only.

### 502 Bad Gateway Error

The model failed to generate a usable result. Retry.

- `any`

### 503 Service Unavailable Error

Simulation generation is paused, no caller personalities are configured, or the quota store is unavailable for a private-key request. Quota outages return SIMULATION_ONBOARDING_QUOTA_UNAVAILABLE with Retry-After; retry with the same key.

- `SimulationOnboardingController_suiteUpdateRequestServiceUnavailableError`
  - SimulationOnboardingErrorDTO: SimulationOnboardingErrorDTO
  - SimulationOnboardingControllerSuiteUpdateRequestServiceUnavailableError1

### 504 Gateway Timeout Error

The edit took too long. Retry with the same `patchKey`.

- `any`

## Types

### SimulationOnboardingSuiteEditDTO

- `simulationId` (string, required) — Simulation in the suite whose scenario is rewritten.
- `instruction` (string, required) — The user's requested change, in their own words.

### SimulationOnboardingSuiteRefDTO

- `id` (string, required) — Suite id; pass it to edit or run the suite.
- `name` (string, required) — Suite name.

### SimulationOnboardingSuiteSimulationDTO

- `id` (string, required) — Simulation id; pass it back to edit or remove the simulation.
- `scenarioId` (string, required) — Scenario the simulation runs.
- `name` (string, required) — Scenario name.
- `successCriteria` (list of string, required) — Each success criterion, as the yes/no question it is judged by.
- `requirementIds` (list of string, required) — Requirement ids this simulation covers, e.g. `R3`.
- `mockedTools` (list of string, required) — Tools the scenario mocks.
- `personalityName` (string, optional) — Name of the simulated caller's personality.

### SimulationOnboardingSuiteCoverageDTO

- `covered` (double, required) — Requirements at least one simulation covers.
- `total` (double, required) — Requirements in the agreed draft.
- `gaps` (list of SimulationOnboardingCoverageGapDTO, required) — Requirements no simulation covers, and why.
- `retryable` (double, required) — Planned scenarios an edit with `retryFailed` would regenerate.

### SimulationOnboardingRequirementDTO

- `id` (string, required) — Stable requirement id, e.g. `R3`.
- `text` (string, required) — One observable behaviour the assistant must get right.
- `tag` (enum, optional) — Why this requirement needs the user's attention, when it does.
  - Allowed values: `assumption`, `not-testable`, `voice-only`
- `note` (string, optional) — What was assumed, why only a voice run can check it, or why it cannot be tested.

### SimulationOnboardingSuiteDocsDTO

- `bestPractices` (string, required) — Link to testing best practices.
- `toolMocks` (string, required) — Link explaining why tools are mocked by default.

### SimulationOnboardingToolGapDTO

- `toolName` (string, required) — Function name of the tool, e.g. `handoff_to_group_1` or `transferCall`.
- `reason` (enum, required) — Why the path is not exercised: a reason the simulations mock the tool, or why a native path is never reached. New reasons may be added.
  - Allowed values: `dynamic-handoff`, `destination-not-plain`, `destination-unresolved`, `destination-truncated`, `transfer-external`, `destination-name-unscoped`, `native-tool-shadowed`
- `assistantName` (string, optional) — Assistant the tool belongs to, when it is a handoff destination's tool. Unset for the assistant's own tools.
- `destinationNames` (list of string, optional) — Static destinations the simulations do not reach through this tool.

### SimulationOnboardingErrorDTO

- `code` (enum, required) — Which refusal this is. Retry `SIMULATION_ONBOARDING_GENERATION_IN_PROGRESS` with the same key and `SIMULATION_ONBOARDING_CONCURRENCY_LIMITED` or `SIMULATION_ONBOARDING_QUOTA_UNAVAILABLE` after `retryAfterSeconds`, send a new key for `SIMULATION_ONBOARDING_IDEMPOTENCY_KEY_REUSED`, and stop for `SIMULATION_ONBOARDING_QUOTA_EXCEEDED`.
  - Allowed values: `SIMULATION_ONBOARDING_GENERATION_IN_PROGRESS`, `SIMULATION_ONBOARDING_IDEMPOTENCY_KEY_REUSED`, `SIMULATION_ONBOARDING_CONCURRENCY_LIMITED`, `SIMULATION_ONBOARDING_QUOTA_EXCEEDED`, `SIMULATION_ONBOARDING_QUOTA_UNAVAILABLE`
- `message` (string, required) — What happened, written for the user.
- `retryAfterSeconds` (double, optional) — Seconds to wait before retrying, the same value as the `Retry-After` header. For a daily limit, the seconds until 00:00 UTC.
- `limit` (double, optional) — The daily limit that was reached, on a daily-limit refusal only.

### SimulationOnboardingControllerSuiteUpdateRequestServiceUnavailableError1

- `statusCode` (double, required)
- `message` (string, required)
- `error` (string, required)

### SimulationOnboardingCoverageGapDTO

- `requirementId` (string, required) — Requirement the gap is for, e.g. `R4`.
- `reason` (enum, required) — Why no simulation covers it. `failed` and `budget` gaps are regenerated by an edit with `retryFailed`.
  - Allowed values: `not-testable`, `unmapped`, `failed`, `budget`
- `detail` (string, optional) — A short explanation for the user.

## Examples

**Request**

```json
{
  "patchKey": "string"
}
```

**Response**

```json
{
  "suite": {
    "id": "string",
    "name": "string"
  },
  "assistantId": "string",
  "simulations": [
    {
      "id": "string",
      "scenarioId": "string",
      "name": "string",
      "successCriteria": [
        "string"
      ],
      "requirementIds": [
        "string"
      ],
      "mockedTools": [
        "string"
      ],
      "personalityName": "string"
    }
  ],
  "coverage": {
    "covered": 1.1,
    "total": 1.1,
    "gaps": [
      {
        "requirementId": "string",
        "reason": "not-testable",
        "detail": "string"
      }
    ],
    "retryable": 1.1
  },
  "requirements": [
    {
      "id": "string",
      "text": "string",
      "tag": "assumption",
      "note": "string"
    }
  ],
  "docs": {
    "bestPractices": "string",
    "toolMocks": "string"
  },
  "reused": true,
  "failedEditSimulationIds": [
    "string"
  ],
  "toolGaps": [
    {
      "toolName": "string",
      "reason": "dynamic-handoff",
      "assistantName": "string",
      "destinationNames": [
        "string"
      ]
    }
  ]
}
```