Flow API
Create, read and change flow agents with the /v2 agent endpoints — the same endpoints and API key as every other agent. The flow format field by field, and every check a save runs.
A flow agent is an agent with "agent_kind": "flow" and a flow. It is created,
read and changed through the same /v2/agents endpoints as every other agent,
with the same API key. A flow sent here opens in the flow builder, and a flow
built in the builder reads back here.
Before you start
| Concern | Rule |
|---|---|
| Base URL | https://api.graine.ai/v2 |
| Authentication | Authorization: Bearer gat_.... See Authentication. |
| Errors | {"error": <code>, "message": "<sentence>"}, as on every /v2 endpoint: branch on error. See Errors. |
| Approvals | In a workspace that reviews agent changes, a key a Member made can get 202 instead of a change: see Approvals. |
| Rate limits | The /v2 limits apply. See Rate limits. |
Endpoints
| Method and path | For a flow agent |
|---|---|
POST /v2/agents | Create one: send "agent_kind": "flow" and a flow. |
GET /v2/agents/{agent_id} | Read it: agent_kind is "flow", and flow is its flow. |
PATCH /v2/agents/{agent_id} | Change it: flow is replaced whole; everything else merges. |
GET /v2/agents | List agents. Each row says its agent_kind; flow is null on a list row, so read the agent for it. |
POST /v2/calls | Call with it as with any agent. The call's variables fill the flow's {names} and the fields its rules read. |
POST /v2/calls/{execution_id}/events | Send an event into the live call: see Events. |
Checking a flow without saving it, and going back to a saved version, are in the flow builder: Check (see Test and fix a flow) and Saved versions (see Save and publish). Through the API, every save runs the same checks.
The flow format
A flow agent is an agent with "agent_kind": "flow" and a flow object. The
flow describes the steps; the rest of the agent (its voice, model, languages,
tools and call settings) is set the way it is for any agent.
Flow
| Field | Type | |
|---|---|---|
schema_version | integer | Always 1 when you read a flow. Optional when you send one. |
start_step | step ID | The first step. Required. |
briefing | text | What every step knows: who the agent is, who it is calling and why. Each AI reply reads it before the step's instructions. |
decision | object | The decision model, below. Optional. |
field_types | object | Gives a rule field a type, string, number or boolean, keyed by the field exactly as rules write it: {"recipient_data.amount_due": "number"}. |
defaults | object | What a call falls back on when it doesn't send a value. A variable's default goes under recipient_data: {"recipient_data": {"business_name": "Acme Finance"}}. A call's own values always win. |
steps | object | Each step, keyed by its ID. At least one. |
A step ID uses letters, digits and _, doesn't start with a digit, and is up to
48 characters. A - is also accepted, for IDs saved before this rule; the
builder doesn't make new ones with it.
Decision
| Field | Type | |
|---|---|---|
provider | text | azure (the default) or sarvam. It can differ from the agent's own provider. |
model | text | A decision model on that provider. Defaults to azure/gpt-4.1-mini on Azure and sarvam/sarvam-105b on Sarvam. |
effort | text | For a GPT-5 decision model: none, low, medium or high. Left out, it thinks as little as the model allows. Not on Sarvam. |
max_tokens | integer | The decision model's maximum answer, above 0. 250 when unset. |
guidance | text | Rules for choosing paths, used on every step in place of the built-in instruction. |
Models offered to flow agents, for replies and for decisions:
| Provider | Models |
|---|---|
azure | azure/gpt-4.1-mini, azure/gpt-4.1-nano, azure/gpt-4o-mini, azure/gpt-4o-mini-2, azure/gpt-5.4-mini, azure/gpt-5.4-nano |
sarvam | sarvam/sarvam-105b, sarvam/sarvam-105b-conversations |
Reasoning effort is none, low, medium or high on the two GPT-5 models and
isn't accepted on the other Azure models. A Sarvam reply model takes low,
medium or high, or no effort to keep thinking off; deciding on Sarvam takes
none. Verbosity, a GPT-5 setting, is low, medium or high.
Step
| Field | Type | |
|---|---|---|
kind | text | converse (an AI reply), say (a fixed line) or route (a branch). converse when unset. |
title | text | The step's name in the builder. |
instructions | text | converse: what the AI does on this step. A route step has none. |
say | text, or language → text | say: the line. One text, or one line per language: {"en": "…", "hi": "…"}. |
sample_replies | language → text | converse: example replies, per language. |
model | object | converse: this step's own reply model, on the agent's provider: model, provider, temperature, max_tokens and effort. Each one left out is the agent's. A step whose model is GPT-5 is saved at temperature 1. |
decision_model | text | This step's own decision model, on decision.provider. |
decision_effort | text | Its effort, for a GPT-5 decision model. |
barge_in_words | integer or null | Words the caller can say over this step before it stops talking, 0 to 10. 0: it can't be interrupted. null: the agent's setting. |
nudge_after_silence_s | number or null | Run the step again after this many seconds of silence, above 0. null: off. |
force_tool | text or null | converse: the tool the AI must call on this step: end_call, or the function_name of one of the agent's tools (call transfer's is transfer_phone). say: end_call hangs up once the line has played, on a step with no next paths. |
knowledge | object | converse: this step's own knowledge base: collections, a list of knowledge bases' retrieval_ids from GET /v2/knowledge-bases, and similarity_top_k, how many passages to read (1 or more). |
next | list | The step's paths. |
The language keys of say and sample_replies are the agent's languages: the
keys of multilingual.languages on an agent that speaks several, else the short
code of its speech recognition language, such as en or hi.
Path
| Field | Type | |
|---|---|---|
to | step ID | Where the path leads. Required. |
label | text | What the arrow says in the builder. |
when | object | When to take the path, below. Required. |
order | integer | Rule paths are checked lowest first, and one without an order counts as 0. Among several always paths, the lowest is the fallback; among event paths for the same event, the lowest is taken. |
collect | name → type, or name → definition | Fields the decision model fills in when it takes this path. A field is its type, string, number, integer or boolean, or a definition: type (default string), description, what the field means, and choices, the answers a string field may take (at least one). The decision model sees the description and the choices on the field. Each field becomes part of the call's data under its name, and its latest value reaches your webhook in collected_fields after the call. A field can't be named reasoning, confidence, recipient_data, detected_language, or start with _. |
tool_name | text | The name the decision model knows this path by: 1 to 64 letters, digits, _ or -. |
tool_description | text | What the decision model reads about this path, in place of when.description. |
When
type | Other fields | |
|---|---|---|
ai | description: text | The decision model takes the path when the description fits. |
rule | match: all (the default) or any; rules: a list | Taken when all, or any, of the rules hold. |
always | none | The fallback when nothing else matches. A route step must have one. |
event | event: the event's name | Taken when that event arrives while the call is on this step. |
Each rule has a field, an op and, for most ops, a value.
op | value |
|---|---|
is, is_not | One value: text, a number, or true / false. |
above, at_least, below, at_most | One number. |
one_of, none_of | A list of values, at least one. |
contains | One value: text that contains it, or a list that holds it. |
is_set, is_not_set | None. Whether the call has the field at all. |
What a rule's field can read, and how values are compared, is on
Using your data.
Layout
Where the builder draws each step is kept by the builder and isn't part of the API. A flow created here is laid out automatically the first time it opens in the builder, and keeps the positions you give it there.
The rest of the agent
| Field | |
|---|---|
agent_kind | "flow". |
display_name | The agent's name. Required when creating. |
greeting_message | The welcome message, said when the call connects. |
configuration.runtime.language_model | The reply model: provider, model and config (temperature, max_tokens, reasoning_effort, verbosity), and the agent's knowledge base as knowledge_base_config (enabled, collections, similarity_top_k). Leave agent_type out: a flow agent's kind is agent_kind, and a change that sets agent_type on a flow agent is refused with 409. |
configuration.runtime.speech_recognition | How the caller is heard. |
configuration.runtime.voice_synthesis | The voice. |
configuration.external_functions | The agent's tools. A step's force_tool can name one by its function_name. |
multilingual | The languages the agent speaks, when more than one. |
The settings in the builder's Settings › On the call are stored where they are for every agent:
| In the builder | Field |
|---|---|
| Ignore the caller until the welcome message is said | configuration.stream_policy.ignore_speech_before_greeting |
| Pause before the welcome message | configuration.stream_policy.greeting_delay_ms (0 to 1000) |
| Words before the caller can interrupt | configuration.stream_policy.bargein_word_threshold |
| Check the caller is still there | configuration.engagement_policy.presence_probe_enabled and presence_probe_after_seconds, and conversation_config.presence_probe_message |
| End the call after a long silence | configuration.session_policy.silence_close_after (seconds; 0 is off) |
| Maximum call length | configuration.session_policy.session_ttl (seconds; 0 is no limit) |
| The agent hangs up when the conversation is over | configuration.session_policy.close_after_reply, and conversation_config.completion_probe_prompt |
| Goodbye message | conversation_config.finale_message |
| Let the caller talk over a closing step | configuration.session_policy.finale_interruptible (false by default) |
| Detect voicemail | configuration.session_policy.completion_probe, and conversation_config.probe_start_delay_seconds |
Create a flow agent
POST /v2/agents. The body is an agent with "agent_kind": "flow" and its
flow. This one is the payment reminder from the overview,
with a fixed line for accounts that owe nothing and a way out for a caller who
goes quiet.
The answer is 201 Created with the new agent, in the same shape a read
returns. A new agent is a draft and starts at version 2, as with any agent.
Its flow is what you sent, with schema_version, briefing and the decision
provider and model filled in where you left them out. Abridged:
Keep agent_id: it is the agent's id everywhere else in the API. Creating takes
longer than a read, so give the request a client timeout of about two minutes.
| Status | When |
|---|---|
201 | Created. |
400 | The body can't be an agent, for example an empty body. |
403 | Your key can't create agents, or the body uses a cloned voice from another member's private Voice Library. |
422 | Code 1001: a field is refused, agent_kind isn't "prompt" or "flow", or the flow has a problem that stops a save (below). Nothing is created. |
429 | Rate limited. Retry after Retry-After seconds. |
503, 504 | A service the request depends on is unavailable or slow. Retry with backoff. |
Read a flow agent
GET /v2/agents/{agent_id} answers with the agent. For a flow agent it has
"agent_kind": "flow" and the flow. A prompt agent has "agent_kind": "prompt"
and "flow": null.
A flow comes back complete: every step has next, every AI reply has
instructions, and schema_version, briefing and the decision provider and
model are always present. A flow saved as
reads back as
Change a flow agent
PATCH /v2/agents/{agent_id} with the fields to change.
flowis replaced whole. A step or path you leave out is deleted, so send the complete flow. The simplest way is to read it, change it and send it back.- Everything else merges into the agent as it does for any agent: send only what changes.
The answer is 200 with the agent as stored. With a problem that stops a save,
it is 422 and nothing is saved.
A live agent. An agent that is live (status ACTIVE) can't be changed: the
answer is 409 with code 1201. Switch it off with {"status": "INACTIVE"} on
its own, change it, and set {"status": "ACTIVE"} again. With a key a Member
made, in a workspace that reviews changes, a change to a live agent becomes a
request for an Admin's approval instead: see Approvals.
Flows saved before a check existed. If a save leaves the flow and the reply model as they are stored (a rename, a new voice), problems in the stored flow don't stop it.
Changing the kind. Send "agent_kind": "flow" with a flow to turn a prompt
agent into a flow agent; its prompt is emptied, and its model must be one offered
for flows. Send "agent_kind": "prompt" with prompts.conversation.system to
turn a flow agent back into a prompt agent; its flow is dropped. Without
agent_kind, an agent keeps its kind, and a flow sent to a prompt agent is
refused.
A flow that uses most features
The flow below books product demos, in English and Hindi. Send it as the
flow of an agent whose languages are English and Hindi; it passes every check
with no warnings. Read it top to bottom:
- Offer a demo answers questions from a knowledge base of its own, repeats
itself after 7 seconds of silence, and gives up after two silent repeats. The
path to Who should run the demo? collects
demo_format, which can only beLiveorRecording. - Who should run the demo? is a branch that never speaks: a rule on the
collected
demo_format, then a rule onteam_size(typed as a number infield_types), then itsalwaysfallback. - Book with sales chooses its paths with its own decision model and effort.
- Find a time replies with shorter answers than the agent's default, can't
be cut in on by fewer than three words, and also moves on when your server
sends the
demo_bookedevent. - Send a recording and No thanks are fixed lines in both languages that hang up after their line; Confirm is an AI reply that ends the call.
- The decision guidance uses a variable, which
defaultsfills when a call doesn't send it.
kb_product_notes stands for the retrieval_id of one of your knowledge
bases, from GET /v2/knowledge-bases. An event that moves the call to
Confirm should carry demo_time in its data, so the goodbye can say it.
Every check
Every create and change that sends a flow runs these checks before anything is
saved. A check that fails stops the save with 422 and code 1001, and the
message counts the problems and quotes the first:
Warnings don't stop a save. To see every problem and warning at once, open the agent in the flow builder and choose Check.
These stop a save:
| What is checked | Rule |
|---|---|
| The flow | flow is an object with at least one step. Every field has the right type: text, a whole number, a list or an object. |
| The first step | start_step is set and names a step. |
| Step IDs | Letters, digits, _ (and -), not starting with a digit or -; up to 48 characters. |
| Step kinds | kind is converse, say or route. say is text or language → text; sample_replies is language → text. |
| Branches | A route step has no instructions and no line, has an always path, and doesn't wait for an event. route steps don't lead to each other in a loop. |
| Paths | next is a list. Each path has a to that names a step, and a when whose type is ai, rule, always or event. |
| Rules | match is all or any. At least one rule; each has a field, a known op, and the value its op takes. |
| Events | An event path names its event. |
| Collected fields | A field is a type, or a definition with only type, description and choices. The type is string, number, integer or boolean. choices is a list of at least one answer, on a string field only. No field is named reasoning or confidence (the decision model returns those with every choice), recipient_data or detected_language, or starts with _. |
| Path names | A tool_name is 1 to 64 letters, digits, _ or -, and isn't end_call, one of the agent's tools, or another name the platform reserves. Two AI-decided paths on one step don't share a name: two that lead to the same step need their own tool_names. |
| Tools | A step's force_tool is end_call or one of the agent's tools. |
| The reply model | provider is azure or sarvam, and the model is one offered for flows. Its reasoning effort is one the model takes. Verbosity is only on GPT-5 models, and is low, medium or high. |
| A step's reply model | On the agent's provider, a model offered there, with a number for temperature, a whole number above 0 for max_tokens, and an effort the model takes. |
| The decision model | decision.provider is azure or sarvam, with a model offered there. Effort only for GPT-5 models, never on Sarvam. max_tokens is a whole number above 0. The same holds for a step's decision_model and decision_effort. |
| Step settings | barge_in_words is a whole number from 0 to 10. nudge_after_silence_s is a number above 0. |
| Knowledge | A knowledge base that is on names at least one library, and similarity_top_k is a whole number of 1 or more. |
| Field types and defaults | field_types maps a field to string, number or boolean. defaults is an object. |
These are warnings, and the save goes through:
| Warning | Why it matters |
|---|---|
An ai path with no description | The decision model has nothing to choose it by. |
A converse step with no instructions | The AI improvises on that step. |
A say step with nothing to say | The step plays nothing. |
A say step without a line for one of the agent's languages | Callers in that language hear the English line, or the first one written. |
A force_tool on a route step; on a say step, any tool but end_call, or end_call while the step has next paths | Only an AI reply runs a tool, so it is ignored there, and a say step with paths moves on after its line instead of hanging up. Make the step a converse step, or remove the say step's paths. |
Two rule or always paths on one step with the same order | Their order is a tie, settled by the order they are listed in. |
| A step that no path leads to from the first step | No call can reach it. |
The flow builder adds a few warnings of its own, never errors: a temperature
outside 0 to 2, a GPT-5 step not at temperature 1, a rule that compares a number
with text or with an empty value, a collected field name that is hard to use in
text, a tool_description without a tool_name, a step with its own decision
model but no AI-decided paths, a line for a language the agent doesn't speak, an
order below 0, and a path listed after an always path.
Approvals
When a key that a Member made changes a live agent, or sets "status": "ACTIVE",
in a workspace that reviews agent changes, the change is held for an Admin and the
answer is 202 Accepted. The live agent is unchanged until an Admin approves, and
then the change is applied exactly as sent. Keys an Admin made, and keys made
before roles existed, never get 202.
The full answer also carries approval_request: the request itself, with its
number, state and a link to it. Sending the same change again before it is
decided updates the same request.
When "status": "ACTIVE" is sent with other fields to an agent that isn't live
yet, the other fields are saved straight away; only going live waits.

