Save Scenarios To Library
Bulk-save selected AI-generated scenarios from a completed run into the library.
This is the bridge between AI generation and the scenario library: within an expanded
run card, a user selects one or more generated scenarios, names each one, applies a
shared set of tags, and saves them all in one action. Each new library scenario row
is populated field-to-field from that scenario’s generated output (origin=ai_saved,
source_run_id set to this run, format=freeform) — not stored as a text blob.
Only available on a completed run (status is complete or partial). A scenario
whose call failed to connect can still be saved — the definition is valid even if the
execution wasn’t; the run status gate is about the run as a whole, not each call.
Atomic: every selection is validated before anything is created. If any single selection is invalid, not found, or already saved, the entire request is rejected and nothing is created.
Authorizations
Use Tuner API key (tr_api_...) or user session token. Find your API key in Workspace Settings > API Keys.
Path Parameters
ID of the simulation run
Tuner's internal numeric ID for the agent.
Workspace ID. Find this in Workspace > General Settings.
Body
Request to bulk-save selected AI-generated scenarios from a completed run into the library (ENG-1540).
One ScenarioSaveSelection per checked row in the run card's multi-select; tags is
a single shared list applied identically to every scenario created by this request.
Response
Successful Response
One past execution of a library scenario, within one simulation run.
Deliberately its own class rather than a reuse of SimulationCallDetailResponse
(app/schemas/simulation_run.py): that model already has a field called
scenario_id meaning "the id of the simulation_run_scenarios row" (the execution
record itself), which is a different concept from the library scenario.id this
endpoint is scoped by. Reusing that model here would silently collide the two
meanings under the same field name.
Carries the same raw ingredients as SimulationCallDetailResponse
(status, call_id, duration, analysis_stopped, evals, type,
target_eval_label) that the web's getScenarioVerdict() helper
(voice-ray-web/src/lib/simulation/displayHelpers.ts) already consumes to compute
pass/fail client-side -- no server-side verdict is computed here, by design.
Reused as-is (ENG-1541) for ScenarioResponse.last_run, the scenario list's additive
"most recent execution" field -- same raw ingredients, one execution either way.