flow, so it is created,
edited, versioned and called with the same API key and the same routes.
Base URL https://sandbox.voice.miraiminds.co. All endpoints require
Authorization: Bearer sk_live_….
When to use a flow
A flow’s transitions are the only functions its model is offered, so a flow
cannot run on-call tools. It can run pre-call and post-call tools.
If a step needs a mid-call lookup, keep that job in a single-prompt agent.
How a flow runs
- The call opens on the start node. Its
greetingis spoken exactly as written, with{{variables}}filled in. With no greeting, the model opens the call from the start node’s prompt. - The model follows the current node. Its instructions are the global
node’s prompt (when the node’s
add_global_promptistrue), then the current node’s prompt. - Transitions are functions. Each transition out of the current node is
offered to the model as a function with no arguments:
labelis its name andconditionis its description. When the model decides the condition is met, it calls the function and the call moves to the target node. - The conversation carries across nodes. Moving to a node replaces the instructions, not the history: the model still knows everything said so far. It then replies as the new node.
- Speech first, then the move. If the model speaks and calls a transition in the same reply, the move happens after that speech has played.
- An end node ends the call. The agent speaks its reply to the end node’s
prompt, then hangs up. The call ends
completedwithended_reason: "assistant-ended-call". - Extraction runs as the call leaves a node. A node with
extraction_enabledhas itsextraction_variablesextracted from the conversation in the background when the call moves on. The node the call ends on is extracted as the call ends.
- Barge-in. While the agent speaks, meaningful speech from the caller cuts
it off. Backchannels such as “हाँ”, “जी” or “ok” do not. On a node with
allow_interrupt: falsethe agent finishes its line, then answers what the caller said. Once a goodbye is playing, nothing the caller says reopens the call. - Silence. After about 10 seconds without the caller speaking, the agent
checks in once. If the caller stays silent, the agent says a short goodbye
and the call ends with
ended_reason: "idle-timed-out". A call created withtiming.user_silence_close_secsuses that timer instead.
Generate a flow from a brief
Describe the agent in plain language and the API drafts the graph for you. Start here, then read and adjust the draft with the schema below.
Nothing is saved. The response is a draft for you to review, test and save.
Each generation is charged to your wallet for the tokens
the model used.
200 OK
Choose a model
Start with the default. Try another model when a draft misses branches or rules
your brief asked for. Whatever the model, set your client’s timeout to a few
minutes; the examples below use 5.
What a draft costs
Each generation is charged to your wallet for the tokens the model used, at the chosen model’s rate per million input and output tokens. The rates are on Billing & tiers.- Every attempt counts. Tokens from every attempt are added up, and the total is rounded up to the paisa once.
- Failures are charged too. A
422 flow_generation_failed, and a503after the model had already answered, are charged for the tokens used. Both responses carryid,model,usageandcost. Nothing is charged when the model never answered. - The wallet must hold a positive balance. An empty wallet is refused with
402 insufficient_balancebefore the model runs. A charge never takes the wallet below zero. - Where it shows. Each charge is one wallet transaction of kind
flow_generation, and the billing statement lists the charges as AI flow drafts.
Generate a draft
- cURL
- Python
- Node.js
Save it on an agent
Create the agent with the draft’sflow and its input_schema. Save them
together: when an agent has an input_schema, every variable a node reads
must be declared in it.
- cURL
- Python
- Node.js
input_schema, each call must send every declared variable in
variables, and nothing else: a missing or undeclared variable is refused with
400 invalid_request. Send number and boolean variables as JSON numbers
and booleans. Then test the agent in the browser
before you dial anyone.
Revise a flow
To change a flow, send the current graph asflow and describe only the
change in brief. The draft keeps what the brief does not ask to change,
including node IDs, transition labels and the editor layout. Save the result
with PATCH, together with its input_schema (the change may read a new
variable), and if_revision so you never overwrite someone else’s edit.
- cURL
- Python
- Node.js
Generation errors
422 uses the same shape as invalid_flow, plus the charge:
422 Unprocessable Entity
503 also
carries id, model, usage and cost beside error. A 503, a 402, and
a 429 sent because the generator is busy do not count against the hourly
limit.
The graph
A flow is a JSON object withnodes, edges and, optionally, viewport.
Nothing else is accepted.
viewport, a node’s position, and an edge’s type and animated are for
visual editors. They accept any JSON value and are returned exactly as sent.
The call ignores them.
Nodes
Node data
Every default is filled in when you save, so a graph you read back states
exactly how each node behaves.
Transitions
Each entry inedges is one transition.
Extraction
An extraction variable is a value the model reads out of the conversation.
Extraction reads the whole conversation up to that point. It runs in the
background when the call leaves the node, so it never delays the agent’s
reply. The node the call ends on is extracted as the call ends. If two nodes
extract the same
name, the later value wins: a correction step can overwrite
what an earlier step heard.
Variables in node text
Node prompts (the global node’s too), greetings and extraction prompts are filled in from the call’svariables.
Transition conditions and labels are never filled in. Write them without
placeholders.
If your agent declares an
input_schema, every variable a node reads must be
declared in it. For a dotted path, its first part must be declared. The time
placeholders need no declaration. An undeclared variable is refused when you
save, with the path of the text that reads it.
Rules
A graph is refused unless all of these hold:- There is exactly one
startCallnode, at least oneendCallnode, and at most oneglobalNode. - Node IDs are unique. Transition IDs are unique. Labels are unique among the transitions leaving one node. Extraction variable names are unique within a node.
- No transition leaves an
endCallnode or theglobalNode, and none enters theglobalNode. - Every node except the global node can be reached from the start node, and has
a path to an
endCallnode. - The graph stays within the limits.
- The agent has no enabled
on_calltools. See Tools. - Your tier’s model makes native tool calls, because every transition is a tool call. The standard model on every tier does.
Build one: a delivery confirmation
This example confirms a delivery slot with a customer, in Hindi. It checks who answered before it mentions the order, offers the slot, finds a new day if needed, and closes with a line the caller cannot cut off. You need: yoursk_live_ key, a phone number you are allowed to call, a
public HTTPS URL for webhooks, and jq for the cURL examples.
Step 1: Write the graph
Save this asdelivery-flow.json.
delivery-flow.json
personaholds what every step shares: who the agent is, the language, reply length, and the rule to move on silently. Each step’s own prompt stays short.identitydiscloses that the caller is speaking with an AI, and says nothing about the order until the right person has confirmed.slotandrescheduleeach name the transitions they may call, and when. Conditions repeat that decision in plain words.goodbyesetsallow_interrupt: false, so the summary of what was agreed is always heard in full.
Step 2: Validate it
input_schema variables are reported when you save the graph on an
agent, because a bare graph has no schema.
- cURL
- Python
- Node.js
200 OK
flow_revision is a fingerprint of the graph: the same graph always has the
same value, and any change produces a new one.
A graph with problems returns every one of them, each with its path:
400 Bad Request
flow on every route, so the same paths work whether you send
the graph to this dry run, to PUT /v2/agents/{id}/flow, or inside an agent.
Reachability (a node that cannot be reached, or has no path to an end) is
checked once the rest of the graph is valid.
Step 3: Create the agent
Send the graph as the agent’sflow. With a flow, system_prompt and
first_message are optional and unused: node prompts give the instructions and
the start node’s greeting opens the call.
- cURL
- Python
- Node.js
201 Created: the agent object, with
revision and the stored flow. Every node comes back with its defaults filled
in:
201 Created (excerpt)
Step 4: Test it in the browser
A browser call is the quickest way to test: you speak to the agent from a web page and watch it move through the graph. Create the call from your backend withchannel: "web" and the variables the nodes read.
- cURL
- Python
- Node.js
id: line of each event is left out above.)
The full event reference is in Browser calls.
Phone calls have no live event stream.
Try each branch before you dial anyone:
Step 5: Call a phone
The same agent takes phone calls. Sendto instead of channel: "web".
- cURL
- Python
- Node.js
202 Accepted, and the call follows the usual
lifecycle and webhooks. To run the same
agent over a list, create a campaign with its agent_id.
Step 6: Read the outcome
A node-based agent’s call carries aflow block: where the call finished, the
steps it went through, and every extracted value. It is on
GET /v2/calls/{id}, on each call in
GET /v2/calls, and at data.call.flow in
call.processed.
Match on
status for your own logic, and keep a default branch: new values can
be added. Values extracted as the call ends are included, so the block is the
complete record of the flow.
When it arrives. The block is absent until the call’s outcome is recorded,
usually within seconds of the call ending, so it can be missing from the
terminal event. A node-based agent’s call always sends call.processed when
the call has a webhook_url (unless the agent sets
emit_processed_webhook: false). That event waits up to 2 minutes for the outcome,
and is sent without flow if it has not arrived by then; read
GET /v2/calls/{id} later in that case.
Edit the graph
Three ways to change a saved graph:GET returns the graph and its identities:
200 OK
GET answers 404 not_found.
To edit safely, read the graph, change it, and save it with
if_revision set to the agent_revision you read. If someone saved the agent
in between, the save is refused with 409 conflict and nothing changes. Read
again and reapply your edit.
- cURL
- Python
- Node.js
200 OK: the updated agent object. PUT applies the same checks as
creating an agent, and reports problems as invalid_flow. On
PATCH, send if_revision in the body instead.
To turn a node-based agent back into a single-prompt agent, send
"flow": null. A single-prompt agent needs a prompt and an opening line, so
send system_prompt and first_message in the same PATCH if the agent has
none:
Versions
The graph is part of the agent, so it is versioned with the agent. There is no separate history for graphs.- Every save creates a revision. Creating the agent, a
PATCH, and aPUT /floweach save a new, immutable agent revision.revisionon the agent is the current one. - Read the history.
GET /v2/agents/{id}/revisionslists revisions newest first;GET /v2/agents/{id}/revisions/{revision}returns one, graph included. - Pin a call.
agent_revisiononPOST /v2/callsruns that saved revision, graph and all, instead of the current one. - Campaigns stay put. A campaign keeps the agent revision it was created with. Editing the graph does not change a campaign that is already running.
- Roll back.
POST /v2/agents/{id}/rollbackwith{"if_revision": <current>, "revision": <old>}copies an old revision into a new draft. A draft takes no ordinary calls. Publish it withPOST /v2/agents/{id}/publishand{"if_revision": <current>}to take calls again.
Calls with a node-based agent
Placing a call, and creating, starting or resuming a campaign, check again
that your tier’s model can run flows. If it cannot, the request is refused
with
400 invalid_flow and nothing is dialled.
Limits
Character limits count characters, not bytes. The size limit is checked on the
graph you send.
Errors
Graph problems use their own shape, like tool validation:error is the string invalid_flow and details lists every problem.
400 Bad Request
Everything else uses the standard error envelope:
Design tips
- One job per node. A node that confirms identity should not also explain the account. Short, single-purpose nodes move more reliably.
- Name the transitions in the node prompt. Say which function to call, and
when: “If they confirm, call
confirmed_identity.” Then write theconditionto match. - Move silently. Tell the model to call a transition without speaking first. The next node’s prompt owns what is said next, so nothing is said twice.
- Make conditions exclusive. Two transitions from one node that could both be true make the model guess. Say what distinguishes them.
- Put shared rules in the global node. Persona, language, reply length and what never to promise belong there once, not in every node.
- Keep end nodes to one line. An end node gets one reply before the call ends. Ask no question there.
- Extract only what you use. Every extraction variable is stored with the call. Do not extract personal data you do not need.
Privacy and compliance
- Say it is an AI. Put the disclosure in the start node’s
greeting, as the example does. See Limits & compliance. - Confirm the person first. Give nothing away until the right person has confirmed, and give a wrong-person branch its own end node.
- Mind what you extract. Extracted values are stored with the call and delivered to your webhook. Treat them like the transcript.
- Calling rules still apply. Calling windows, consent and do-not-call rules are the same for every agent. See India calling rules.
Troubleshooting
Related
Agents
Every agent field, including
flow.Browser calls
Audio, live events and control for browser calls.
Calls
Place calls, read status, transcripts and recordings.
Tools
Pre-call and post-call tools for node-based agents.