Skip to content

TEST-203 docs: add the Simulations onboarding API guide - #1344

Draft
scott-lowe-vapi wants to merge 2 commits into
scottlowe/test-201-simulations-docs-catch-upfrom
scottlowe/test-203-onboarding-api-guide
Draft

scott-lowe-vapi wants to merge 2 commits into
scottlowe/test-201-simulations-docs-catch-upfrom
scottlowe/test-203-onboarding-api-guide

Conversation

@scott-lowe-vapi

Copy link
Copy Markdown
Contributor

Description

Draft: merge when VapiAI/vapi#23422 (TEST-200) deploys. That PR publishes the onboarding endpoints and lists this guide as a merge gate. Linear: TEST-203. Stacked on #1342.

  • New page Generate a test suite with the API (/observability/simulations-generate-api), added to the nav after Advanced. It covers:
    • beta key access, and the 404 for orgs without it
    • the five-step flow with cURL: draft, revise, generate with idempotencyKey, edit with patchKey, run with Idempotency-Key
    • request and response field tables
    • coverage and tool gap reasons
    • idempotency and retries: the in-progress and key-reused 409s
    • daily limits: 60 drafts, 20 generations and 60 edits per UTC day, at most 2 generating at once, with what doesn't count
    • the error table
  • Advanced: troubleshooting rows for the TEST-192 run-create 400 (VapiAI/vapi#23407) and the Failed to create target call failure reason (VapiAI/vapi#23400).
  • Composer: a Limitations bullet on the shared daily allowance.

Sources are the open stack's diffs (#23400, #23407, #23418–#23422). The 524 in the retry list comes from the endpoint's own operation description.

Before merge:

  • Re-check every value against the merged code.
  • Resolve the MDX TODO in Limits: do the limits apply to Enterprise orgs, and can they be raised?
  • Confirm "contact support" is the right path for beta key access.
  • Link the API reference pages once fern api update pulls the routes in.

What's New blurb:

Generate a simulation suite with the API (beta). Three new endpoints under /eval/simulation/onboarding run the same test-generation flow as Composer: draft numbered requirements from an assistant's prompt, tools, and handoffs, revise them with your feedback, then generate a saved suite whose simulations mock every tool that would reach an external system. The response reports which requirements are covered, which aren't and why, and any handoffs or transfers the simulations don't exercise. Edit the suite in place, then run it with POST /eval/simulation/run; an idempotencyKey makes every retry safe. Private API keys work for organizations enabled during the beta, so contact support to turn it on, and generation shares a free daily allowance per organization with Composer that resets at 00:00 UTC. Read Generate a test suite with the API.

Testing Steps

  • fern check (5.151.1) passes: 0 errors. The 14 warnings were already on main.
  • Check the preview deployment once the endpoints are live on staging

@github-actions

Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

scott-lowe-vapi and others added 2 commits October 18, 2026 21:45
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@scott-lowe-vapi
scott-lowe-vapi force-pushed the scottlowe/test-203-onboarding-api-guide branch from ef56047 to b6e5fd4 Compare October 19, 2026 04:46
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

Comment thread fern/composer.mdx
- **Cannot make business logic decisions**: You provide the domain knowledge; Composer handles the technical configuration.
- **Cannot test every behavior**: Suites Composer generates run in chat, so they can't check voice-only behavior such as pronunciation, and [some handoffs and transfers](/observability/simulations-advanced#handoffs-and-transfers-in-simulations) aren't tested. Make test calls yourself for these.
- **Cannot deploy to production automatically**: You control when and how changes go live.
- **Daily limits on test generation**: Requirement drafts, suite generations, and suite edits share a free daily allowance per organization with the [Simulations onboarding API](/observability/simulations-generate-api#limits). When your organization reaches a limit, Composer tells you and doesn't retry. The limits reset at 00:00 UTC.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subject is three nouns, and "with the API" comes at the end.

Suggested change
- **Daily limits on test generation**: Requirement drafts, suite generations, and suite edits share a free daily allowance per organization with the [Simulations onboarding API](/observability/simulations-generate-api#limits). When your organization reaches a limit, Composer tells you and doesn't retry. The limits reset at 00:00 UTC.
- **Daily limits on test generation**: Each organization gets a free daily allowance of requirement drafts, suite generations, and suite edits. Composer and the [Simulations onboarding API](/observability/simulations-generate-api#limits) share it. When you reach a limit, Composer tells you and doesn't retry. Limits reset at 00:00 UTC.

slug: observability/simulations-generate-api
---

The Simulations onboarding API builds a [**simulation suite**](/observability/simulations-overview) for an assistant from its prompt, tools, and handoffs. It's the same flow [**Composer**](/composer#generate-tests-for-an-assistant) runs when you ask it to generate tests, exposed as three endpoints you can call from a script, a CI job, or a coding agent:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The Simulations onboarding API builds a [**simulation suite**](/observability/simulations-overview) for an assistant from its prompt, tools, and handoffs. It's the same flow [**Composer**](/composer#generate-tests-for-an-assistant) runs when you ask it to generate tests, exposed as three endpoints you can call from a script, a CI job, or a coding agent:
The Simulations onboarding API builds a [**simulation suite**](/observability/simulations-overview) for an assistant from its prompt, tools, and handoffs. It runs the same flow as [**Composer**](/composer#generate-tests-for-an-assistant). Call these three endpoints from a script, a CI job, or a coding agent:

You then run the suite with the existing [`POST /eval/simulation/run`](/observability/simulations-quickstart) endpoint.

<Note>
**Beta.** Private API keys can call these endpoints only for organizations enabled during the beta. For any other organization they return `404`. [Contact support](/support) to enable your organization. Composer and the Dashboard work without this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Beta.** Private API keys can call these endpoints only for organizations enabled during the beta. For any other organization they return `404`. [Contact support](/support) to enable your organization. Composer and the Dashboard work without this.
**Beta.** During the beta, these endpoints only work for organizations we've enabled. Other organizations get a `404`. [Contact support](/support) to enable yours. You don't need this to use Composer or the Dashboard.

- An HTTP client with a long timeout. Suite generation can take up to 3 minutes, so set a client timeout of at least 200 seconds.

<Warning>
Generated simulations mock every tool in the assistant's tool list that would reach an external system, and the response lists them in `mockedTools`. Tools that only an assistant hook calls aren't mocked, and the assistant's server messages, such as the end-of-call report, still go to its server URL. Tools you add to a simulation later, or mocks you turn off, run for real. See [**Mock tool responses**](/observability/simulations-advanced#mock-tool-responses).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Four ideas in two sentences (and it repeats Advanced). Bullets:

Suggested change
Generated simulations mock every tool in the assistant's tool list that would reach an external system, and the response lists them in `mockedTools`. Tools that only an assistant hook calls aren't mocked, and the assistant's server messages, such as the end-of-call report, still go to its server URL. Tools you add to a simulation later, or mocks you turn off, run for real. See [**Mock tool responses**](/observability/simulations-advanced#mock-tool-responses).
Generated simulations mock every tool in the assistant's tool list that would reach an external system. The response lists them in `mockedTools`. These still run for real:
- Tools that only an assistant hook calls.
- Server messages, such as the end-of-call report, which still go to the assistant's server URL.
- Tools you add to a simulation later, or mocks you turn off.
See [**Mock tool responses**](/observability/simulations-advanced#mock-tool-responses).

}
```

Review the requirements and answer the open questions. A requirement tagged `assumption` rests on a guess; one tagged `not-testable` describes something a chat simulation can't check, such as pronunciation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split at the semicolon:

Suggested change
Review the requirements and answer the open questions. A requirement tagged `assumption` rests on a guess; one tagged `not-testable` describes something a chat simulation can't check, such as pronunciation.
Review the requirements and answer the open questions. A requirement tagged `assumption` is based on a guess. A requirement tagged `not-testable` is something a chat simulation can't check, such as pronunciation.

| `destination-truncated` | The destination wasn't checked because the assistant has too many handoff destinations to check in time, or it didn't load in time. The handoff into it is mocked. |
| `transfer-external` | A `transferCall` can leave Vapi, to a phone number, a SIP address, or a destination your server chooses, so it is mocked. |
| `destination-name-unscoped` | A handoff names a destination assistant outside a squad, which can't be resolved at runtime, so that destination is never reached. The handoff isn't mocked. |
| `native-tool-shadowed` | A tool that normally runs as is (such as `endCall` or `transferCall`) shares its name with a mocked tool, so the mock answers it too. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"A tool that normally runs as is" is awkward. Maybe: "A built-in tool, such as endCall or transferCall, has the same name as a mocked tool, so the mock answers it too."


A retry that returns a saved suite sets `reused` to `true` and doesn't count toward your limits. To generate a new suite from a revised draft, use a new key.

**Edits.** Send a new `patchKey` for each edit. Replaying a key that was already applied changes nothing and returns the current suite, with `failedEditSimulationIds` listing what that edit couldn't rewrite. So you can retry a timeout or a `5xx` with the same key. A suite remembers its last 50 patch keys, so never reuse one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contradicts itself: it says to retry a timeout with the same key, then ends "so never reuse one".

Suggested change
**Edits.** Send a new `patchKey` for each edit. Replaying a key that was already applied changes nothing and returns the current suite, with `failedEditSimulationIds` listing what that edit couldn't rewrite. So you can retry a timeout or a `5xx` with the same key. A suite remembers its last 50 patch keys, so never reuse one.
**Edits.** Send a new `patchKey` for each edit, and the same key to retry it. Replaying a key that was already applied changes nothing. It returns the current suite, with `failedEditSimulationIds` listing what that edit couldn't rewrite. A suite remembers only its last 50 patch keys, so never reuse a key for a different edit.


## Limits

Each organization has a free daily allowance per UTC day, shared by the API, the Dashboard, and Composer. Limits reset at 00:00 UTC.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a free daily allowance per UTC day" says "daily" twice. Maybe: "Each organization has a free daily allowance, shared by the API, the Dashboard, and Composer. Limits reset at 00:00 UTC."


| Status | When | What to do |
| -- | -- | -- |
| `400` | The body is invalid; `previousDraft` and `feedback` were not sent together; or the generated or edited suite uses something your organization's data-region policy doesn't allow, so it isn't saved. | Fix the request. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cell (and the 422 row below) lists several causes separated by semicolons and parentheses. Consider one row per cause, or <br /> between causes.

| --- | --- |
| Every iteration fails | Review the transcript, then verify the evaluation's description, data type, comparator, and expected value. |
| A run stays queued or running | Check the assistant or squad configuration and its provider credentials. Fetch the run and its items to find a failure reason. |
| Creating a voice run returns `400 Target assistant would fail to start a call: <message>` | The inline assistant in `target` has a configuration a call would reject, such as a tool type the model doesn't support. Fix what the message names, then create the run again. Vapi checks this only for voice runs with an inline assistant target, not for a saved `assistantId`, a squad, or a chat run. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: "Vapi checks this only for voice runs…" → "Vapi runs this check only for voice runs with an inline assistant target. It doesn't check a saved assistantId, a squad, or a chat run."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants