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

# Test and fix a flow

> Tests a graph with simulated calls, reviews what happened, fixes what the review found and tests the fix. Nothing is saved. It answers 202 straight away with a job that usually finishes in 2–6 minutes: poll Get a flow test.

What to test with: `simulate` invented callers written from your `brief` (2 by default), `examples` (your real calls, each caller replayed against the graph), `calls` (transcripts of test calls you made), or a mix. `rounds` is the most revisions to make (1–3), and `verify` tests each revision again. `variables` are test values for the call variables. `agent_id` links the job to an agent so you can find it later.

Simulated calls are text only: no phone rings and no tool runs. This charges your wallet once, when the job ends, for every model call it made. 10 per workspace per hour.



## OpenAPI

````yaml /api-reference/openapi.json post /v2/agents/flow/refine
openapi: 3.1.0
info:
  title: Mirai Voice API
  version: 2026-10-15.analysis-templates
  description: >-
    The v2 API for voice agents, calls, campaigns, phone numbers and speech.
    Every request carries `Authorization: Bearer sk_live_…`. Generated from the
    API's own contract and its Postman collection.
servers:
  - url: https://sandbox.voice.miraiminds.co
    description: 'Sandbox: trial workspaces, before your company goes live'
  - url: https://prod.voice.miraiminds.co
    description: 'Production: workspaces that have gone live'
security:
  - bearerAuth: []
tags:
  - name: Agents
    description: >-
      An agent is what a call runs: its prompt (or a node flow, see the Flows
      section), voice, language and limits. Write it once and dial it as often
      as you like; each call fills in its own `variables`.


      Every save creates a new revision. You can read old revisions, roll back
      to one, and publish a draft when it is ready. None of the requests here
      place a call or charge your wallet.
  - name: Calls
    description: >-
      Place a call, follow it while it runs, then read what happened: the
      transcript, the recording, the post-call analysis.


      `POST /v2/calls` answers before the phone rings, with an id. Everything
      after that is about that id. Live control and the live event stream work
      on browser calls (`channel: "web"`) and realtime sessions only; a phone
      call answers them with `400 unsupported_channel`.
  - name: Flows
    description: >-
      A node-based agent runs the call as a graph of steps. Each node has its
      own instructions, and the agent moves to the next node when a transition's
      condition is met, so a call with branches (wrong person, not now,
      reschedule) stays easy to follow and to change.


      The usual path: draft a graph from a brief or from recordings of your real
      calls, check it, test it with simulated calls, then save it on an agent.
      Drafting, transcribing and testing use AI models and charge your wallet;
      checking, saving and reading are free.


      Full guide: https://docs.miraiminds.co/v2/node-agents
  - name: Tools and secrets
    description: >-
      Connect your own APIs to an agent. A tool runs at one of three moments:


      * `pre_call`, before dialling: fetch context for the greeting, or skip the
      call.

      * `on_call`, when the agent decides to use it mid-conversation: look
      something up, book something.

      * `post_call`, after the call has settled: send the outcome to your CRM,
      schedule a call back.


      The order here is the order you'd use them in: see what's available, store
      your credential as a secret, add a tool, test it against your endpoint,
      then read each call's tool runs. Tools themselves cost nothing extra.


      Tool definitions use Mirai's own templates, like `{{ args.date }}` and `{{
      secrets.crm_key }}`.


      A definition that doesn't validate comes back as a list, so you can fix
      everything at once: `{"error": "invalid_tools", "details": [{"path",
      "message"}]}`. Everything else uses the usual `{"error": {"code",
      "message"}}`.
  - name: Campaigns
    description: >-
      Call a whole list with one agent. Upload contacts, say which days and
      hours the campaign may ring, and start it. From then on it dials by
      itself: it paces itself, waits overnight, retries calls that didn't
      connect, skips numbers on your do-not-call list, and pauses itself when
      its budget or your wallet runs out.


      Campaign calls are ordinary calls. They show up in `GET /v2/calls`, send
      the same `call.*` webhooks and bill the same per-minute rate. In a sandbox
      workspace every campaign runs and bills at T5 (₹5 a minute), whatever tier
      you ask for.


      Money on a campaign is in **paise**: `"budget_paise": 50000` is ₹500.


      The requests run top to bottom: create saves `campaign_id`, and the rest
      use it. Starting a campaign rings every contact on it, so put numbers
      you're allowed to call in the bodies first.
  - name: Do-not-call list
    description: >-
      Numbers your campaigns will never dial. The list covers every campaign in
      the workspace, including ones that are already running: it's checked at
      the moment a contact is about to be called, so a number added mid-campaign
      is skipped from then on. A skipped contact stays in its campaign as
      `suppressed` and costs nothing.


      It doesn't apply to single calls placed with `POST /v2/calls`; check the
      list yourself before those if you need to. It's your own list, not the
      national DND register, and adding a number here doesn't meet your DND
      obligations.
  - name: Phone numbers
    description: >-
      The numbers your agents answer. Each one comes from the **Telephony**
      section:


      * **Rented from Mirai.** Once the rental is active, the number shows up
      here by itself (`ownership: managed`).

      * **From your own Plivo account.** Connect the account in Telephony, list
      its numbers, then bring one here with [Import a Plivo
      number](/api-reference/phone-numbers/import-a-plivo-number) (`ownership:
      customer`). Plivo keeps billing you for it.


      After that it's two steps, in this order: [Assign an
      agent](/api-reference/phone-numbers/assign-an-agent) (who answers), then
      [Turn on incoming
      calls](/api-reference/phone-numbers/turn-on-incoming-calls). `state` is
      `imported` until calls are on and `ready` after; `error` means the last
      attempt to turn calls on failed, and `setup_error` says why.


      Every change sends the number's current `version`, and each change raises
      it by one. If you're not sure what it is, read the number first; a stale
      version is a 409 `conflict` and changes nothing.


      To call out from one of your numbers, place a call with `phone_number_id`
      set to its id once it is `ready` (Indian destinations only). Calls on your
      numbers are billed like any other call.


      Every number also says how it is doing in `health`: `state` is `active`,
      `warming` (its first week, at half its pool's pace), `resting`, `cooling`
      (rested three times in a day; waits for you) or `retired`, with this
      hour's attempts and seven days of answers. You can rest, resume and retire
      your own numbers; a resting or retired number still answers calls to it.
  - name: Phone pools
    description: >-
      A pool is a group of your own phone numbers that a campaign, an agent or a
      single call can ring from. Each call takes the number best placed for it:
      a number a contact has already been rung from stays theirs on a retry,
      numbers in the contact's own circle come first, and the pool's pace
      (`max_per_hour`, per number) is held across every campaign and API call at
      once. A number whose answers drop well below its pool-mates' rests on its
      own for 45 minutes.


      Point a campaign at a pool with `pool_id` on [Create a
      campaign](/api-reference/campaigns/create-a-campaign), an agent with
      `calls_from: {"pool_id": …}`, or one call with `pool_id` on [Place a
      call](/api-reference/calls/place-a-call). `pool_id` and `phone_number_id`
      never go together. With nothing chosen, calls come from the agent's
      setting, then the pool marked `default_for_campaigns`, then the Mirai
      shared line. A call's `calls_from_source` is `pool` (with `pool_id`) when
      a pool picked its number.


      A single call on a pool whose numbers are all resting or at their pace
      this hour answers 409 `pool_busy` with `Retry-After`; one with no number
      that can call, 409 `pool_unavailable`. A campaign doesn't fail: it waits
      and carries on.


      `purpose` is what the calls are for: `outreach` (promotional),
      `reminders`, `support` or `other`. It sets the starting pace: 40 an hour
      for support, 60 otherwise. Pools are free; numbers are billed as numbers.
  - name: Telephony
    description: >-
      Getting a phone number for your agent. There are two ways, and a workspace
      can use both.


      **1. Rent a number from Mirai.** ₹500 a month, paid as ₹1,500 from your
      wallet every three months, renewing until you turn it off. Indian numbers
      need a business verification that Plivo approves once: read the
      requirements, submit your details and documents, then refresh until it's
      `accepted`. After that: search, quote, rent. The rented number appears in
      **Phone numbers** by itself.


      **2. Connect your own Plivo account.** Plivo keeps billing you directly.
      Connect it with your Auth ID and Auth Token, list the numbers it owns, and
      bring one over with [Import a Plivo
      number](/api-reference/phone-numbers/import-a-plivo-number). A Plivo main
      account can also rent new numbers through the same search → quote → rent
      requests, with its connection id as `account`.


      Either way you finish in **Phone numbers**: assign the agent that answers,
      then turn on incoming calls.


      The last two requests verify your own phone, so the workspace can ring it
      from Mirai's shared test number (test calls are placed from the Numbers
      page in the console).


      Indian (+91) numbers only for now. If phone numbers aren't available on
      the host you're calling, every request here answers 503
      `telephony_unavailable`.
  - name: Transcriptions
    description: >-
      Turn a recorded call (or any recording) into a transcript with speakers
      and timestamps. Hindi, English, Gujarati and mixed speech are written the
      way they were spoken, never translated. ₹18 per hour of audio; failed and
      degraded jobs are free.
  - name: Speech to text
    description: >-
      Speech to text in ten languages, two ways: send one recording and get its
      text back in the same request ([Transcribe a
      recording](/api-reference/speech-to-text/transcribe-a-recording)), or
      stream live audio over one WebSocket per call and read partial transcripts
      while people speak, then one final transcript per utterance. Language
      codes: hi-IN, en-IN, gu-IN, mr-IN, pa-IN, bn-IN, ta-IN, te-IN, kn-IN or
      ml-IN.


      `wss://sandbox.voice.miraiminds.co/v1/audio/transcriptions/stream?language_code=hi-IN&encoding=mulaw&sample_rate=8000&endpointing=manual`


      (`wss://prod.voice.miraiminds.co` once your company has gone live), with
      the header `Authorization: Bearer sk_live_YOUR_API_KEY`. 8 or 16 kHz,
      16-bit PCM, μ-law or A-law. A browser uses `?token=` from [Mint stream
      tokens](/api-reference/speech-to-text/mint-stream-tokens) instead, because
      it cannot set headers on a WebSocket.


      Send JSON text frames: `{"event": "speech_start"}`, then `{"event":
      "audio_input", "audio": "<base64 audio>"}` as the audio arrives, then
      `{"event": "speech_end"}`; `{"event": "end"}` closes the session. You
      receive `session.begin` first, `transcript.partial` while an utterance is
      open, exactly one `transcript.final` per utterance, and `session.end` with
      `audio_seconds_billed` and `cost_paise`.


      Every event, both endpointing modes, limits and errors:
      https://docs.miraiminds.co/v2/stt-quickstart
  - name: Text to speech
    description: >-
      Turn text into speech in one request, and read back what each request
      cost.
  - name: Wallet
    description: >-
      Your prepaid balance and every line that moved it. Calls, text to speech,
      flow drafts, transcriptions and number rentals all debit the same wallet.
  - name: Reference
    description: The voice catalogue, and the API contract as a machine-readable document.
paths:
  /v2/agents/flow/refine:
    post:
      tags:
        - Flows
      summary: Test and fix a flow
      description: >-
        Tests a graph with simulated calls, reviews what happened, fixes what
        the review found and tests the fix. Nothing is saved. It answers 202
        straight away with a job that usually finishes in 2–6 minutes: poll Get
        a flow test.


        What to test with: `simulate` invented callers written from your `brief`
        (2 by default), `examples` (your real calls, each caller replayed
        against the graph), `calls` (transcripts of test calls you made), or a
        mix. `rounds` is the most revisions to make (1–3), and `verify` tests
        each revision again. `variables` are test values for the call variables.
        `agent_id` links the job to an agent so you can find it later.


        Simulated calls are text only: no phone rings and no tool runs. This
        charges your wallet once, when the job ends, for every model call it
        made. 10 per workspace per hour.
      operationId: refine_agent_flow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              additionalProperties: false
              description: >-
                The graph to test and what to test it with: calls (test calls
                you made), simulate (simulated calls), or both. At least one
                test: with neither, 400 invalid_request.
              properties:
                agent_id:
                  description: >-
                    Optional: the agent this test is for, one of this
                    workspace's agents (404 not_found otherwise). The job
                    records it, and GET /v2/agents/{id}/flow/refinement answers
                    the agent's latest job for 24 hours, so a client can let the
                    test run on in the background.
                  maxLength: 64
                  minLength: 1
                  type: string
                brief:
                  description: >-
                    Optional: the business's requirements for the agent (the
                    brief it was generated from). The review judges the flow
                    against it and a revision keeps to it. Treated as data,
                    never as instructions to the API.
                  maxLength: 20000
                  type: string
                calls:
                  description: >-
                    Optional: 1-5 transcripts of test calls you made with this
                    flow, one AGENT:/CALLER: turn per line (POST
                    /v2/agents/flow/transcribe turns a recording into one). The
                    review reads them as real calls (speech recognition errors
                    are not held against the flow); a revision is verified by
                    replaying each call's caller against it.
                  items:
                    additionalProperties: false
                    properties:
                      transcript:
                        description: >-
                          One call, one turn per line: AGENT: (the agent) or
                          CALLER:. At least one turn.
                        maxLength: 40000
                        minLength: 1
                        type: string
                    required:
                      - transcript
                    type: object
                  maxItems: 5
                  minItems: 1
                  type: array
                examples:
                  description: >-
                    Optional: transcripts of the business's real calls (2-10,
                    together at most 200,000 characters), as given to generate.
                    Their callers are replayed against the flow (simulate), and
                    the review checks each replay against its original:
                    coverage, order and what the original agent did well.
                  items:
                    additionalProperties: false
                    properties:
                      transcript:
                        description: >-
                          One call, one turn per line: AGENT: (the business's
                          side) or CALLER:.
                        maxLength: 40000
                        minLength: 1
                        type: string
                    required:
                      - transcript
                    type: object
                  maxItems: 10
                  minItems: 2
                  type: array
                flow:
                  $ref: '#/components/schemas/Flow'
                  description: >-
                    The graph to test, as GET /v2/agents/{id}/flow or POST
                    /v2/agents/flow/generate returns it. Validated like PUT
                    /flow (400 invalid_flow).
                language:
                  description: >-
                    Optional language code or name for the spoken lines (for
                    example hi, en or Hinglish). Without it the brief decides.
                  maxLength: 32
                  type: string
                max_duration_secs:
                  description: >-
                    Optional: the agent's call time limit. The review flags a
                    main path that cannot finish inside it.
                  maximum: 1800
                  minimum: 30
                  type: integer
                model:
                  default: gemini-3.8-flash
                  description: >-
                    The model that reviews the test calls and revises the graph:
                    gemini-3.8-flash (Gemini 3.8 Flash), claude-opus-5.5 (Claude
                    Opus 5.5) or gpt-6-sol (GPT-6 Sol). Omitted:
                    gemini-3.8-flash. Simulated calls do not run on it: both
                    sides run on the model your tier's live calls run on
                    (falling back to gemini-3.8-flash when that is unavailable).
                  enum:
                    - gemini-3.8-flash
                    - claude-opus-5.5
                    - gpt-6-sol
                  type: string
                rounds:
                  default: 1
                  description: >-
                    The most revisions to make. Default 1: test, review, revise
                    once, then re-test and review the revision to report what
                    remains.
                  maximum: 3
                  minimum: 1
                  type: integer
                simulate:
                  description: >-
                    Simulated calls in the first round. With examples they are
                    REPLAYS of the examples (the original callers played again
                    against this flow): by default every example up to 5, the
                    most different first, and never more than there are
                    examples. Without examples they are invented callers: 2 by
                    default. With calls the default is 0 (the calls are the
                    test). Every later round runs the same callers again, so
                    before and after are comparable.
                  maximum: 5
                  minimum: 0
                  type: integer
                variables:
                  additionalProperties:
                    maxLength: 2000
                    type:
                      - string
                      - number
                      - boolean
                  description: >-
                    Optional test values for the call variables the graph reads
                    as {{name}} (each key at most 64 characters). Every
                    simulated caller gets plausible synthetic values for the
                    rest, consistent with the caller.
                  maxProperties: 50
                  type: object
                verify:
                  default: true
                  description: >-
                    Re-test the graph after each revision with the same
                    simulated callers (and a replay of every real call's caller,
                    at most 5 replays a round) and review it again; revise again
                    while rounds remain. false: one review and at most one
                    revision, not re-tested.
                  type: boolean
              required:
                - flow
              type: object
            examples:
              Test and fix a flow:
                summary: Test and fix a flow
                value:
                  brief: >-
                    Call Mirai Motors customers whose car is due for service and
                    book a workshop visit. Speak everyday Hindi. First confirm
                    you are speaking to the customer by name; if it is someone
                    else, apologise and end the call without mentioning the car.
                    Then say which car is due for service and on what date, and
                    ask which day they can bring it in. Read the day back and
                    end the call. If they don't want to book now, thank them and
                    end. Record the day they choose.
                  language: hi
                  model: gemini-3.8-flash
                  simulate: 1
                  rounds: 1
                  verify: true
                  variables:
                    name: राहुल
                    vehicle: स्विफ्ट
                    due_date: 18 अगस्त
                  max_duration_secs: 300
                  agent_id: agt_01K2M4P7QRSTVWXYZ0A1B2C3
                  flow:
                    nodes:
                      - id: persona
                        type: globalNode
                        data:
                          name: Persona and rules
                          prompt: >-
                            You are Ashu, a service advisor at Mirai Motors.

                            Speak everyday Hindi in Devanagari; common English
                            words such as service, slot and workshop are fine.

                            Say one or two short sentences, then listen.

                            Never promise a discount, a price or a pickup.

                            When the condition of one of your functions is met,
                            call it at once without saying anything first.
                      - id: identity
                        type: startCall
                        data:
                          name: Confirm the customer
                          greeting: >-
                            नमस्ते, मैं मिराई मोटर्स से आशु बोल रहा हूं. क्या
                            मेरी बात {{name}} जी से हो रही है?
                          prompt: >-
                            You have asked whether you are speaking with
                            {{name}}. Wait for the answer.

                            If they confirm, call confirmed_identity.

                            If it is someone else or a wrong number, call
                            wrong_person.

                            Do not mention the car or the service before they
                            confirm.
                      - id: offer_slot
                        type: agentNode
                        data:
                          name: Book the service
                          prompt: >-
                            Tell them their {{vehicle}} is due for service on
                            {{due_date}}, and ask which day they can bring it to
                            the workshop.

                            When they name a day, read it back once and call
                            slot_booked.

                            If they do not want to book now, call not_now.
                          extraction_enabled: true
                          extraction_variables:
                            - name: preferred_day
                              type: string
                              prompt: >-
                                The day the customer chose for the service, in
                                their words, for example Saturday or 20 August.
                      - id: goodbye
                        type: endCall
                        data:
                          name: Close
                          prompt: >-
                            In one sentence, repeat what was agreed. Thank them
                            and say goodbye. Do not ask anything else.
                          allow_interrupt: false
                      - id: not_customer
                        type: endCall
                        data:
                          name: Wrong person
                          prompt: >-
                            Apologise for the trouble and say goodbye. Do not
                            mention the car or the service.
                    edges:
                      - id: e1
                        source: identity
                        target: offer_slot
                        data:
                          label: confirmed_identity
                          condition: >-
                            The caller confirmed they are the customer you asked
                            for.
                      - id: e2
                        source: identity
                        target: not_customer
                        data:
                          label: wrong_person
                          condition: >-
                            The caller is someone else, or says it is a wrong
                            number.
                      - id: e3
                        source: offer_slot
                        target: goodbye
                        data:
                          label: slot_booked
                          condition: >-
                            The customer named a day for the service and you
                            read it back.
                      - id: e4
                        source: offer_slot
                        target: goodbye
                        data:
                          label: not_now
                          condition: The customer does not want to book the service now.
      responses:
        '202':
          description: Started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowRefinement'
              examples:
                Started:
                  summary: 202 — started
                  value:
                    id: frf_01KZR3Q5S89NTXQ40V3KYN4JMA
                    object: flow_refinement
                    status: running
                    stage: writing_scenarios
                    progress:
                      round: 1
                      rounds: 2
                      tests_done: 0
                      tests_total: 1
                    model: gemini-3.8-flash
                    simulation_model: voice-agent
                    agent_id: agt_01K2M4P7QRSTVWXYZ0A1B2C3
                    input_flow_revision: 2c0eccdffdf0edca4e0a
                    created_at: '2026-08-11T09:51:05Z'
                    updated_at: '2026-08-11T09:51:05Z'
                    finished_at: null
                    examples: []
                    calls: []
                    rounds: []
                    found: []
                    remaining: []
                    usage:
                      prompt_tokens: 0
                      completion_tokens: 0
                    cost:
                      amount_inr: 0
        '400':
          description: Nothing to test
          content:
            application/json:
              examples:
                Nothing to test:
                  summary: 400 — nothing to test
                  value:
                    error:
                      code: invalid_request
                      message: >-
                        nothing to test: give calls (1–5 transcripts of test
                        calls you made), simulate (1–5 simulated calls), or both
        '402':
          description: Empty wallet
          content:
            application/json:
              examples:
                Empty wallet:
                  summary: 402 — empty wallet
                  value:
                    error:
                      code: insufficient_balance
                      message: >-
                        wallet balance 0.00 INR is empty; testing a flow needs a
                        positive balance
components:
  schemas:
    Flow:
      additionalProperties: false
      description: >-
        A node graph: the conversation as nodes with their own instructions and
        transitions between them. See x-semantics.flow for the rules.
      properties:
        edges:
          items:
            $ref: '#/components/schemas/FlowEdge'
          maxItems: 40
          minItems: 1
          type: array
        nodes:
          items:
            $ref: '#/components/schemas/FlowNode'
          maxItems: 20
          minItems: 2
          type: array
        viewport:
          description: Editor camera. Any JSON; stored and returned as sent.
      required:
        - nodes
        - edges
      type: object
      x-max-bytes: 100000
    FlowRefinement:
      properties:
        agent_id:
          description: The agent the request named, or null.
          type:
            - string
            - 'null'
        calls:
          description: >-
            The test calls the job was given, tidied to one AGENT:/CALLER: turn
            per line as they were tested; tests[].source.index (kind call)
            points into this list. Kept with the job (24 hours), readable only
            by its workspace; never logged.
          items:
            properties:
              transcript:
                type: string
            required:
              - transcript
            type: object
          maxItems: 5
          type: array
        changed:
          description: 'Once succeeded: whether flow differs from the graph sent.'
          type: boolean
        cost:
          description: >-
            What the job costs: every simulated turn on the live-call model at
            the text channel's LLM rates (₹14.38 / ₹57.52 per million input /
            output tokens), every hosted call (the callers, a fallback, the
            reviews and revisions) at the provider's reported cost × ₹88 × 1.2
            (its per-token rates when none is reported), rounded up once to a
            paisa. So far while running; once finished (succeeded, failed or
            canceled), the amount charged to the wallet in one flow_generation
            transaction described "Flow test · <model> · … tokens".
          properties:
            amount_inr:
              minimum: 0
              type: number
          required:
            - amount_inr
          type: object
        created_at:
          format: date-time
          type: string
        error:
          description: Why a failed job failed.
          properties:
            code:
              enum:
                - upstream_unavailable
                - timeout
                - review_failed
                - interrupted
                - internal
            message:
              type: string
          required:
            - code
            - message
          type: object
        examples:
          description: >-
            The example calls the job was given, as it used them, in order: a
            replay's tests[].source.index (kind example) points into this list,
            so a client can show each replay beside its original, and resending
            them with the current flow tests the same calls again. Kept with the
            job (24 hours), readable only by its workspace; never logged.
          items:
            properties:
              transcript:
                type: string
            required:
              - transcript
            type: object
          maxItems: 10
          type: array
        finished_at:
          format: date-time
          type:
            - string
            - 'null'
        flow:
          $ref: '#/components/schemas/Flow'
          description: >-
            Once succeeded: the final graph — the last valid revision, or the
            graph sent when nothing was changed (changed false). It passes every
            rule PUT /v2/agents/{id}/flow applies. Not saved.
        flow_revision:
          description: 'Once succeeded: the final graph''s flow revision.'
          type: string
        found:
          description: What the first review found in the graph sent.
          items:
            $ref: '#/components/schemas/FlowFinding'
          type: array
        id:
          description: >-
            The job id (GET /v2/agents/flow/refine/{id}) and the reference of
            its wallet transaction (kind flow_generation). Quote it in a support
            request.
          pattern: ^frf_[0-9A-Z]{26}$
          type: string
        input_flow_revision:
          description: >-
            The flow revision of the graph the job was given (the graph under
            test).
          type: string
        input_schema:
          description: >-
            Once succeeded: the variables as an agent input_schema, ready to
            save beside the flow.
          properties:
            properties:
              additionalProperties:
                properties:
                  description:
                    type: string
                  type:
                    enum:
                      - string
                      - number
                      - boolean
                type: object
              type: object
            required:
              items:
                type: string
              type: array
            type:
              const: object
          required:
            - type
            - properties
            - required
          type: object
        model:
          description: The model that reviews and revises.
          enum:
            - gemini-3.8-flash
            - claude-opus-5.5
            - gpt-6-sol
          type: string
        object:
          const: flow_refinement
        progress:
          description: >-
            round: the test round under way (1-based); rounds: the most there
            can be (1, plus 1 per revision when verify is true: 2 by default);
            tests_done of tests_total: this round's tests.
          properties:
            round:
              minimum: 1
              type: integer
            rounds:
              maximum: 4
              minimum: 1
              type: integer
            tests_done:
              minimum: 0
              type: integer
            tests_total:
              minimum: 0
              type: integer
          required:
            - round
            - rounds
            - tests_done
            - tests_total
          type: object
        remaining:
          description: >-
            What the last review found: with verify, in the final graph;
            without, in the graph sent.
          items:
            $ref: '#/components/schemas/FlowFinding'
          type: array
        rounds:
          description: The test rounds finished so far, in order.
          items:
            $ref: '#/components/schemas/FlowRefinementRound'
          maxItems: 4
          type: array
        simulation_model:
          description: >-
            The model simulated calls run on: the LLM your tier's live calls run
            on, as that stack names it (for example voice-agent), or
            gemini-3.8-flash when that model was unavailable, switched off or
            failed during the job (the job then falls back for good; each test
            names its model).
          type: string
        stage:
          description: >-
            What a running job is doing; done once it succeeded; where it
            stopped once it failed or was canceled.
          enum:
            - writing_scenarios
            - simulating
            - reviewing
            - revising
            - verifying
            - done
        status:
          description: >-
            canceled: stopped with POST /v2/agents/flow/refine/{id}/cancel; what
            had answered is charged and the rounds that finished are kept.
          enum:
            - running
            - succeeded
            - failed
            - canceled
        updated_at:
          format: date-time
          type: string
        usage:
          description: >-
            Every model call's tokens, simulated turns included, summed. So far
            while running.
          properties:
            completion_tokens:
              minimum: 0
              type: integer
            prompt_tokens:
              minimum: 0
              type: integer
          required:
            - prompt_tokens
            - completion_tokens
          type: object
        variables:
          description: >-
            Once succeeded: the call variables the final graph reads as
            {{name}}. When nothing changed, every one is typed string.
          items:
            properties:
              description:
                maxLength: 500
                type: string
              name:
                pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
                type: string
              type:
                enum:
                  - string
                  - number
                  - boolean
            required:
              - name
              - type
              - description
            type: object
          maxItems: 32
          type: array
      required:
        - id
        - object
        - status
        - stage
        - progress
        - model
        - simulation_model
        - agent_id
        - input_flow_revision
        - created_at
        - updated_at
        - finished_at
        - examples
        - calls
        - rounds
        - found
        - remaining
        - usage
        - cost
      type: object
    FlowEdge:
      additionalProperties: false
      properties:
        animated:
          description: Editor field. Any JSON; stored and returned as sent.
        data:
          additionalProperties: false
          properties:
            condition:
              description: >-
                When to take it; given to the model as the function's
                description. Must not be blank.
              maxLength: 2000
              minLength: 1
              type: string
            label:
              description: >-
                The function name the model calls to take this transition.
                Unique among the source node's transitions.
              pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
              type: string
          required:
            - label
            - condition
          type: object
        id:
          description: Unique in the graph.
          minLength: 1
          type: string
        source:
          description: A node id; not an endCall or the globalNode.
          type: string
        target:
          description: A node id; not the globalNode.
          type: string
        type:
          description: Editor field. Any JSON; stored and returned as sent.
      required:
        - id
        - source
        - target
        - data
      type: object
    FlowNode:
      additionalProperties: false
      properties:
        data:
          $ref: '#/components/schemas/FlowNodeData'
        id:
          description: Unique in the graph.
          pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
          type: string
        position:
          description: Editor layout. Any JSON; stored and returned as sent.
        type:
          description: >-
            startCall opens the call (exactly one); endCall ends it after its
            reply (at least one, no outgoing transitions); globalNode is a
            prompt prepended to every node with add_global_prompt (at most one,
            no transitions).
          enum:
            - startCall
            - agentNode
            - endCall
            - globalNode
          type: string
      required:
        - id
        - type
        - data
      type: object
    FlowFinding:
      properties:
        evidence:
          description: The test and the lines that show it.
          maxLength: 1200
          type: string
        fix:
          description: The change to the graph that fixes it.
          maxLength: 1200
          type: string
        nodes:
          description: Ids of the nodes the fix changes.
          items:
            type: string
          type: array
        severity:
          description: >-
            high: the call fails its purpose or the caller is re-asked, stuck or
            annoyed; medium: noticeably unnatural or fragile; low: polish
            (reported, never revised automatically).
          enum:
            - high
            - medium
            - low
        title:
          maxLength: 160
          type: string
      required:
        - severity
        - title
        - evidence
        - fix
        - nodes
      type: object
    FlowRefinementRound:
      properties:
        findings:
          description: What the review of these tests found, most severe first.
          items:
            $ref: '#/components/schemas/FlowFinding'
          maxItems: 12
          type: array
        flow_revision:
          description: The revision of the graph these tests ran on.
          type: string
        revised:
          description: >-
            A revision was made from the high and medium findings; the next
            round tests it (or it is the result).
          type: boolean
        revision_issues:
          description: >-
            The revision stayed invalid after repairs, so the graph tested here
            was kept and the job stopped.
          items:
            properties:
              message:
                type: string
              path:
                type: string
            required:
              - path
              - message
            type: object
          type: array
        tests:
          items:
            $ref: '#/components/schemas/FlowTest'
          type: array
        verdict:
          description: ready exactly when no high or medium finding remains.
          enum:
            - ready
            - needs_changes
      required:
        - flow_revision
        - tests
        - findings
        - verdict
        - revised
        - revision_issues
      type: object
    FlowNodeData:
      additionalProperties: false
      properties:
        add_global_prompt:
          default: true
          type: boolean
        allow_interrupt:
          default: true
          type: boolean
        extraction_enabled:
          default: false
          type: boolean
        extraction_variables:
          default: []
          items:
            $ref: '#/components/schemas/FlowVariable'
          maxItems: 12
          type: array
        greeting:
          description: >-
            Exact line spoken on entering the node; the start node's greeting
            opens the call.
          maxLength: 1500
          type:
            - string
            - 'null'
        is_start:
          description: Derived from type on save.
          type: boolean
        name:
          maxLength: 80
          minLength: 1
          type: string
        prompt:
          description: >-
            The node's instructions, including when to call each outgoing
            transition. {{variables}} are rendered per call.
          maxLength: 12000
          minLength: 1
          type: string
        tools:
          description: >-
            Names of this agent's on_call tools the node offers the model beside
            its transitions, in order. startCall and agentNode only; never
            end_call and never a transition label. Omitted when empty.
          items:
            pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
            type: string
          maxItems: 10
          type:
            - array
            - 'null'
          uniqueItems: true
      required:
        - name
        - prompt
      type: object
    FlowTest:
      properties:
        ended:
          description: >-
            How a simulated call ended: an end node spoke its reply; the caller
            hung up; the agent spoke 20 times; the agent kept moving between
            nodes (4 transitions, or 8 function calls in one turn, without the
            caller speaking); or a technical failure cut it short (error says
            why). Absent for a real call.
          enum:
            - end_node
            - caller_hung_up
            - turn_limit
            - transition_loop
            - error
        error:
          type: string
        estimated_secs:
          description: >-
            How long the call takes spoken: its words at about 150 a minute plus
            a second a turn.
          minimum: 0
          type: integer
        kind:
          description: >-
            simulated: a text call with a simulated caller; real: one of the
            request's calls.
          enum:
            - simulated
            - real
        model:
          description: >-
            The model that played both sides of a simulated call: the live-call
            model, or gemini-3.8-flash after a fallback (a call the live-call
            model failed is run again from the start there). Absent for a real
            call.
          type: string
        name:
          type: string
        nodes_visited:
          description: >-
            The names of the nodes a simulated call visited, in order; empty for
            a real call.
          items:
            type: string
          type: array
        scenario:
          description: >-
            The simulated caller: who they are, what they want, how they act and
            speak, the call variables the call ran with (synthetic, and your
            variables) and, for a replay, the synthetic values that stood in for
            the original's redactions ([NAME] …).
          properties:
            behaviour:
              type: string
            goal:
              type: string
            language:
              type: string
            name:
              type: string
            persona:
              type: string
            placeholders:
              additionalProperties:
                type: string
              type: object
            variables:
              type: object
          type: object
        source:
          description: >-
            Where the test comes from. kind example: a replay of
            examples[index]; kind call: calls[index] itself (a real test) or its
            caller replayed (a simulated test); kind invented: the index-th
            invented caller. index is 0-based.
          properties:
            index:
              minimum: 0
              type: integer
            kind:
              enum:
                - example
                - call
                - invented
          required:
            - kind
            - index
          type: object
        transcript:
          description: >-
            The call in order. agent: the agent spoke (node_id/node_name: the
            node it spoke at; text may be empty when it said nothing); caller:
            the caller spoke; transition: the agent moved on (text: the
            function, node_id/node_name: the node it led to); tool: the agent
            asked for an on-call tool, which does not run in a test.
          items:
            properties:
              node_id:
                type: string
              node_name:
                type: string
              role:
                enum:
                  - agent
                  - caller
                  - transition
                  - tool
              text:
                type: string
            required:
              - role
              - text
            type: object
          type: array
      required:
        - kind
        - name
        - source
        - transcript
        - nodes_visited
        - estimated_secs
      type: object
    FlowVariable:
      additionalProperties: false
      properties:
        name:
          description: Unique within the node.
          pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
          type: string
        prompt:
          maxLength: 2000
          minLength: 1
          type: string
        type:
          enum:
            - string
            - number
            - boolean
          type: string
      required:
        - name
        - type
        - prompt
      type: object
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Your workspace secret key, `sk_live_` followed by 32 hex characters.
        Create one in the console under **Developers**.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.