Skip to main content
A node-based agent (a flow) runs a conversation as a graph. Each node is one step of the call with its own instructions. Each transition between nodes is a condition, written in plain language, that the model checks as the caller speaks. When a condition is met, the call moves to the next step and that step’s instructions take over. Node-based agents are available on every tier. They take phone calls, browser calls and campaigns, and they are billed like any other call. A flow is one field on the agent, 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

  1. The call opens on the start node. Its greeting is spoken exactly as written, with {{variables}} filled in. With no greeting, the model opens the call from the start node’s prompt.
  2. The model follows the current node. Its instructions are the global node’s prompt (when the node’s add_global_prompt is true), then the current node’s prompt.
  3. Transitions are functions. Each transition out of the current node is offered to the model as a function with no arguments: label is its name and condition is its description. When the model decides the condition is met, it calls the function and the call moves to the target node.
  4. 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.
  5. 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.
  6. An end node ends the call. The agent speaks its reply to the end node’s prompt, then hangs up. The call ends completed with ended_reason: "assistant-ended-call".
  7. Extraction runs as the call leaves a node. A node with extraction_enabled has its extraction_variables extracted from the conversation in the background when the call moves on. The node the call ends on is extracted as the call ends.
Interruptions and silence follow the same rules as every agent:
  • 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: false the 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 with timing.user_silence_close_secs uses 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
Review and test every draft before it takes a real call. Read each node’s prompt and each transition’s condition, and check that the draft asks, records and refuses what you intended. Then run a browser test with live events and take every branch at least once.

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 a 503 after the model had already answered, are charged for the tokens used. Both responses carry id, model, usage and cost. Nothing is charged when the model never answered.
  • The wallet must hold a positive balance. An empty wallet is refused with 402 insufficient_balance before 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

Save it on an agent

Create the agent with the draft’s flow and its input_schema. Save them together: when an agent has an input_schema, every variable a node reads must be declared in it.
With an 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 as flow 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.
Test the revised agent again before it takes real calls. A revision is a new agent revision like any other save, so you can roll it back.

Generation errors

422 uses the same shape as invalid_flow, plus the charge:
422 Unprocessable Entity
The other errors use the standard envelope. A charged 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 with nodes, 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 in edges is one transition.
Write labels in lowercase snake_case, such as confirmed_identity. The model sees each label in lowercase, and a readable name helps it choose.

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’s variables. 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 startCall node, at least one endCall node, and at most one globalNode.
  • 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 endCall node or the globalNode, and none enters the globalNode.
  • Every node except the global node can be reached from the start node, and has a path to an endCall node.
  • The graph stays within the limits.
  • The agent has no enabled on_call tools. See Tools.
  • Your tier’s model makes native tool calls, because every transition is a tool call. The standard model on every tier does.
Problems are reported with the path of each. See errors.

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: your sk_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 as delivery-flow.json.
delivery-flow.json
What each part does:
  • persona holds 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.
  • identity discloses that the caller is speaking with an AI, and says nothing about the order until the right person has confirmed.
  • slot and reschedule each name the transitions they may call, and when. Conditions repeat that decision in plain words.
  • goodbye sets allow_interrupt: false, so the summary of what was agreed is always heard in full.

Step 2: Validate it

The body is the graph. The dry run applies every graph rule and checks that your tier’s model can run flows. It saves nothing and calls no one. Undeclared input_schema variables are reported when you save the graph on an agent, because a bare graph has no schema.
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
Paths start at 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’s flow. 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.
201 Created: the agent object, with revision and the stored flow. Every node comes back with its defaults filled in:
201 Created (excerpt)
The other agent fields work as they do on any agent, with two differences:

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 with channel: "web" and the variables the nodes read.
Open the two links from a page. The browser example is a complete page; add two listeners to show the current step and what each step heard:
Your backend can read the same stream with the API key. An abridged test where the customer asks for another day:
(The 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. Send to instead of channel: "web".
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 a flow 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
A single-prompt agent has no graph: 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.
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:
An edit takes effect on the next call. Calls already queued or running keep the graph they started with.

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 a PUT /flow each save a new, immutable agent revision. revision on the agent is the current one.
  • Read the history. GET /v2/agents/{id}/revisions lists revisions newest first; GET /v2/agents/{id}/revisions/{revision} returns one, graph included.
  • Pin a call. agent_revision on POST /v2/calls runs 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}/rollback with {"if_revision": <current>, "revision": <old>} copies an old revision into a new draft. A draft takes no ordinary calls. Publish it with POST /v2/agents/{id}/publish and {"if_revision": <current>} to take calls again.
Publishing and rolling back check the graph again, including your tier’s model.

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
Problems are found in stages: first the graph’s own rules, then the agent’s (declared variables and on-call tools), then your tier’s model. Each stage reports all of its problems at once. Fix them and save again to see the next stage. 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 the condition to 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

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.