All docs

Reference · Flow agents

Flow step handbook

Every step you can put on a flow canvas, what it does, where its links go, and an everyday picture to remember it by.

Picture your flow agent as a very reliable receptionist with a binder of scripts you approved. It never improvises. Each step is one card in that binder, and each link (an arrow on the canvas) tells the receptionist which card to turn to next.

A hybrid agent uses the same binder, with an AI colleague at the next desk. Anything your flows don’t cover goes to the AI, which can also start one of your tasks. One step, AI answer, lets a hybrid flow ask that colleague for a single reply. The other 16 steps work the same in both kinds of agent.

Cards that talkSay something, then turn the page straight away.
Cards that askAsk the visitor and wait for the answer.
Cards that decideCheck something and pick the next card. The visitor sees nothing.
Cards that finishClose the folder, or hand it to a colleague.

Only three steps make the agent stop and wait for the visitor: Buttons, Ask for and Confirm. Every other step runs instantly, and a whole chain of them can run between two messages. Each card below is marked Waits for visitor or Runs instantly. New to flows? Start with Build your first flow agent.

Where a conversation starts

Before any step runs, something has to open the binder. On the canvas these are the dashed start badges, not steps. Each one points at the first step to run.

Intent

The signs above the counters at the post office: "Parcels", "Passports", "Payments".

A task the visitor can ask for, with a button label and keywords. "I lost my card" matches the keyword "lost card" and goes straight to that task's first step.

Anything else (fallback)

The information desk for everyone who walks in without knowing which counter they need.

Where a message goes when no intent matches and nothing is under way. Usually a menu of Buttons.

Keyword command

The help point on a train platform: it works wherever you're standing.

Words such as agent or start over that jump to a step from anywhere, even in the middle of a question.

Say

Steps that put words in front of the visitor without asking anything.

Message

messageRuns instantly
Think of it as

The receptionist reading one line off a script card, then turning to the next card without pausing.

What it does
Sends your text, then carries straight on. Several Messages in a row arrive as one reply bubble.
Use it for
Answers to simple questions, an explanation before a question, a disclaimer.
Links out
Then
Settings
The text, up to 2,000 characters. It can include saved answers, like {{ phone }}, and filters, like {{ amount | format_amount:2 }}.

Leave Then empty and the task is finished. The next message the visitor sends is matched against your intents again.

›JSON
"hours": {
  "type": "message",
  "text": "We're open 9am to 5pm, Monday to Friday.",
  "next": "menu"
}

Ask

The only steps that stop and wait. Each one checks the answer, asks again if it doesn't fit, and has a way out for a visitor who never gives a usable answer. A question nobody answers for 30 minutes is forgotten, and the next message starts fresh.

Buttons

choiceWaits for visitor
Think of it as

The buttons in a lift. You can only go to the floors that exist, and each button takes you somewhere different.

What it does
Asks a question and shows up to 6 buttons. Each button has its own link. The visitor can press one, type its label, or type one of its keywords.
Use it for
Menus, and any question with a short, fixed set of answers.
Links out
One per buttonNo match
Settings
  • Question, and button labels (up to 40 characters each).
  • Keywords per button, so “agent” can count as Talk to a person.
  • Save the choice as a variable (optional).
  • Retry text and attempts (2 by default, up to 5) before taking No match.

On email, where buttons can’t be shown, the agent sends a numbered list and accepts the number as the answer.

›JSON
"menu": {
  "type": "choice",
  "prompt": "Hi! What can I help you with?",
  "options": [
    { "label": "Opening hours", "next": "hours" },
    { "label": "Talk to a person", "keywords": ["agent"], "next": "to_human" }
  ],
  "on_fail": "to_human"
}

Ask for

collectWaits for visitor
Think of it as

A bank clerk filling in one box on a form, who won't move on until what you said really fits the box.

What it does
Asks for one piece of information, checks it, and saves it under a name you choose. If it doesn't fit, it explains and asks again.
Use it for
Phone numbers, emails, dates, amounts, order references, choosing a card from a list your system sent back.
Links out
Got itSkippedGave up
What it accepts
  • Any text, with a minimum and maximum length.
  • A number or an amount of money, with optional limits.
  • An email address or a phone number.
  • A date: day-first or month-first, past and future allowed or not.
  • Yes or no.
  • One of a list (buttons you write), or one of a list from a variable, such as the cards an API call returned (up to 6 shown).
  • Text matching a pattern, such as ACC-\d{6}.
Settings
  • Optional adds a Skip button and the Skipped link.
  • An answer the visitor already gave isn’t asked for again, unless you tick Ask even if it’s already answered.
  • Retry text and attempts, as for Buttons.

Saved answers are reusable. After slot: "phone", any later text can say {{ phone }}.

›JSON
"ask_phone": {
  "type": "collect",
  "slot": "phone",
  "prompt": "Sure. Which number should we call?",
  "entity": { "kind": "phone" },
  "ask_again": true,
  "next": "confirm_call",
  "on_fail": "to_human"
}

Confirm

confirmWaits for visitor
Think of it as

The pharmacist reading your prescription back to you before handing it over.

What it does
Reads something back and asks yes or no. Yes and No each have their own link.
Use it for
Right before anything that changes something: freezing a card, booking a call, cancelling an order.
Links out
YesNoNo answer
Settings
The question, the two button labels (rename them, e.g. Freeze it / Keep it), and attempts.

Link No back to the question that collected the detail, so the visitor can correct it rather than start over.

›JSON
"confirm_call": {
  "type": "confirm",
  "prompt": "We'll call you on {{ phone }}. Is that right?",
  "yes": "call_booked",
  "no": "ask_phone",
  "on_fail": "to_human"
}

Decide

Steps that choose the next card without saying anything. The visitor never sees them, only where they lead.

Condition

conditionRuns instantly
Think of it as

Signposts at a junction, read from the top: take the first road whose sign fits, and the main road if none do.

What it does
Checks rules in order and follows the first one that’s true. If none are, it follows Otherwise.
Use it for
Checking what an API call returned ("is the card already frozen?"), or comparing an answer against a limit.
Links out
One per ruleOtherwise
Comparisons
is, is not, is less than, is at most, is more than, is at least, is one of, is not one of, contains, is set, is not set. A rule can combine several checks with all of or any of.

Order matters. Put the most specific rule first, because the first match wins. Nested groups and “not” can be written in the JSON view.

›JSON
"check_balance": {
  "type": "condition",
  "branches": [
    { "when": { "var": "balance", "op": "lt", "value": 100 }, "next": "low_balance" }
  ],
  "else": "show_balance"
}

Switch

switchRuns instantly
Think of it as

A mail sorter's pigeonholes: one slot per postcode, and a tray for everything else.

What it does
Looks at one variable and follows the link for its value (capital letters don’t matter). Any other value follows Otherwise.
Use it for
Routing on a plan, a country, an order status: one value, many destinations.
Links out
= each valueOtherwise
Settings
The variable, and up to 30 values, each with its own link.

Switch or Condition? Use Switch when you’re asking “which value is it?”. Use Condition when you’re asking “is this true?” or comparing sizes.

›JSON
"route_status": {
  "type": "switch",
  "var": "order_status",
  "cases": { "shipped": "track", "processing": "be_patient" },
  "default": "to_human"
}

Once

onceRuns instantly
Think of it as

The gym's welcome tour: newcomers get it on their first visit, and after that everyone just gets a nod at the door.

What it does
Follows First time the first time a conversation reaches it, and Every time after from then on.
Use it for
A welcome, a privacy notice or a tip that shouldn't repeat every time the visitor comes back to the menu.
Links out
First timeEvery time after
Settings
None besides the two links. It remembers per conversation.
›JSON
"greet": {
  "type": "once",
  "first": "welcome",
  "after": "menu"
}

Remember

Steps that change what the agent knows, quietly.

Set variables

set_variableRuns instantly
Think of it as

A sticky note on the folder: jot something down to use later, or peel one off when it's no longer true.

What it does
Saves values into variables, or clears them, then carries on.
Use it for
Tidying an answer ({{ name | title }}), building a value from others, or clearing old answers before asking again.
Links out
Then
Settings
Up to 30 values to set and 30 to clear. Filters: upper, lower, trim, title, round:N, format_amount:N, format_date:FORMAT, mask, truncate:N, default:TEXT.

Lists stay lists. A value that is exactly one variable, like {{ cards }}, keeps its type instead of becoming text.

›JSON
"tidy_up": {
  "type": "set_variable",
  "set": { "total": "{{ amount | round:2 }}" },
  "clear": ["card"],
  "next": "confirm_pay"
}

Connect

Steps that reach outside the conversation: your own systems, and your website's login.

API call

httpRuns instantly
Think of it as

The receptionist phoning the back office to check something. Either someone answers with the facts, or the line is dead and there's a plan for that too.

What it does
Calls one of your systems over HTTPS, saves fields from the reply into variables, and follows a link based on how it went.
Use it for
Looking up an order, listing a customer's cards, freezing a card, creating a ticket or a booking.
Links out
One per ruleSuccessError
Settings
  • A saved tool from Settings → Tools, or a request written here: method, URL, query, headers and a JSON body.
  • Save maps reply fields to variables, e.g. $.status or $.cards.
  • Rules can test the status code and reply fields before Success.
  • API keys go in Secrets and are used in headers only, as {{ secret.NAME }}.
Safety rules
HTTPS only, public addresses only, no redirects, at most 10 seconds and 1 MB. Any non-2xx reply, timeout or blocked address takes Error. Each call counts toward your monthly tool calls.

Always link Error to something kind, like an apology and a handoff. Systems go down; the visitor shouldn’t be stuck when they do.

›JSON
"lookup": {
  "type": "http",
  "request": {
    "method": "GET",
    "url": "https://api.example.com/orders/{{ order_id }}",
    "headers": { "Authorization": "Bearer {{ secret.shop_key }}" }
  },
  "save": { "order_status": "$.status" },
  "next": "route_status",
  "on_error": "api_down"
}

Verified only

require_identityRuns instantly
Think of it as

Showing your ID at the bank counter before they'll discuss your account.

What it does
Continues only if your website has told Flexaura who the logged-in visitor is, with a signed token. Anonymous visitors take Not verified.
Use it for
The step before anything that reads or changes the visitor's own account.
Links out
VerifiedNot verified
Settings
None. It needs Verified visitors set up on the flow page (see Verify who the visitor is). After it, text can use {{ user.name }}, {{ user.email }} and {{ user.id }}.

Publishing checks this for you. An API call that sends {{ user.* }} must come after a Verified only step on every path, or the flow won’t publish.

›JSON
"must_log_in": {
  "type": "require_identity",
  "next": "list_cards",
  "on_fail": "please_log_in"
}

Report

Steps that tell your team what happened, without the visitor seeing anything.

Notify by email

notify_emailRuns instantly
Think of it as

The receptionist dropping a note in a colleague's pigeonhole: "Ada wants a call back on this number." Then straight back to the visitor.

What it does
Emails up to 5 people on your team, then carries on. The visitor isn't kept waiting and doesn't see it.
Use it for
Callback requests, new claims or leads, an order someone needs to check by hand: anything a person should act on after the chat.
Links out
Then
Settings
  • Send to: 1–5 fixed addresses. Visitors can never change who gets the email.
  • Subject and Message: plain text with saved answers, e.g. Callback request: {{ phone }}. A link to the conversation is added at the bottom.
Limits
At most 3 emails per conversation. Each one counts as a tool call; after the monthly limit the email is skipped and the flow carries on.

Handoff email or this? The handoff email tells your team a visitor is waiting in the chat now. Notify by email is for work after the chat, while the agent carries on by itself.

›JSON
"tell_team": {
  "type": "notify_email",
  "to": ["callbacks@acme.com"],
  "subject": "Callback request: {{ phone }}",
  "body": "Please call {{ phone }} within one working day.",
  "next": "call_booked"
}

Mark goal

mark_goalRuns instantly
Think of it as

A tally mark on the whiteboard behind the desk: one more card frozen, one more callback booked.

What it does
Records that this conversation reached an outcome you care about, then carries on. Visitors don't see it.
Use it for
The moment a task succeeds: right before the End step of "freeze a card" or "book a callback".
Links out
Then
Settings
A goal id such as callback_booked, and an optional name for reports. Steps with the same id count as one goal, and a conversation counts once however often it gets there.

Mark the success, not the attempt. Put it after the API call’s Success link, so failed attempts don’t count.

›JSON
"booked": {
  "type": "mark_goal",
  "goal": "callback_booked",
  "label": "Callback booked",
  "next": "tell_team"
}

Hybrid agents only

A deterministic agent never uses a generative model, so this step can only be added to a hybrid agent's flow. Publishing refuses it anywhere else.

AI answer

llm_answerHybrid onlyRuns instantly
Think of it as

The receptionist turning to the expert at the next desk: "Could you explain our fees to this customer?" The expert answers in their own words, and the receptionist carries on with the script.

What it does
The agent's AI writes one reply, using its prompt, its knowledge base and the conversation so far, and the flow moves straight on. The AI doesn't ask the visitor anything here.
Use it for
Questions whose answer lives in your documents and differs from visitor to visitor: explaining fees, a policy, a product, or what happens next after a task.
Links out
Then
Settings
What should the AI answer?, up to 1,000 characters, e.g. “Explain our delivery fees from the knowledge base.” It can include saved answers like {{ plan }}, which are sent to the AI. Secrets can’t be used here.
If it fails
If the AI can't be reached, or its reply is flagged by moderation, the step says nothing and the flow carries on. Nothing is handed off.
Cost
The reply is priced by the agent's model, like any AI-written reply, instead of the flow's 1 credit.

Keep exact wording in Message steps. Use AI answer only where an answer in the AI’s own words is fine. Disclaimers, prices you’ve agreed and anything legal belong in a Message, word for word. Saved tests show [AI answer] in place of the reply and never call the AI.

›JSON
"explain_fees": {
  "type": "llm_answer",
  "instruction": "Explain our delivery fees from the knowledge base.",
  "next": "anything_else"
}

Move

Steps that jump to another part of the flow.

Go to

gotoRuns instantly
Think of it as

"Now turn to page 12" in an instruction booklet. You go there and carry on from that page; you don't come back.

What it does
Jumps to a step, or to the start of an intent (main flow only).
Use it for
"Anything else?" loops back to the menu, or sending a visitor from one task into another.
Links out
Go toIntent: name
Settings
Exactly one target: a step or an intent.

Loops are fine as long as they pass through a question. Publishing blocks a loop that never waits for the visitor.

›JSON
"back_to_menu": { "type": "goto", "to": "menu" }
"start_freeze": { "type": "goto", "to_intent": "freeze_card" }

Sub-flow

run_subflowRuns instantly
Think of it as

A recipe that says "make the pastry (see page 40)". You follow the pastry recipe, then come back to where you were with the pastry in hand.

What it does
Runs a separate, reusable piece of flow, then returns and continues at When it’s done.
Use it for
Anything you'd otherwise build twice, such as checking a date of birth before both freezing a card and changing an address.
Links out
When it's done
Settings
  • Which sub-flow to run. Each sub-flow has its own tab on the canvas.
  • Inputs: values handed in, e.g. card_label ← {{ card.label }}.
  • Outputs: values handed back, saved under a name in this flow.

A sub-flow keeps to itself. It sees only the inputs you pass it, and only the outputs you map come back. An End step inside it returns to the caller.

›JSON
"verify": {
  "type": "run_subflow",
  "subflow": "verify_dob",
  "inputs": { "card_label": "{{ card.label }}" },
  "outputs": { "verified": "dob_ok" },
  "next": "check_verified"
}

Finish

Steps with no link out. The task stops here.

Hand off

handoffRuns instantly
Think of it as

"Let me get my manager." The receptionist hands over the folder with a note on top, and steps back.

What it does
Sends an optional message, then passes the conversation to your team. From then on the agent stays quiet until someone clicks Return to bot in Conversations.
Use it for
“Talk to a person”, every No match, Gave up and Error link, and anything the script shouldn’t handle.
Links out
None: the conversation is with your team now
Settings
  • Message (optional): what the visitor is told, e.g. “I’m passing you to our team. Someone will reply here shortly.”
  • Reason: shown to your team in Conversations (“Handed to your team: …”) and in the handoff email, never to the visitor.

Set the Handoff email on the flow page so someone actually hears about it. The email includes the reason, recent messages and the answers the flow collected.

›JSON
"to_human": {
  "type": "handoff",
  "message": "I'm passing you to our team. Someone will reply here shortly.",
  "reason": "Visitor asked for a person"
}

End

endRuns instantly
Think of it as

"Thanks, have a good day", closing the folder, and if you tick the box, stamping it Done.

What it does
Sends an optional last message and finishes the task. The visitor can still start another one.
Use it for
The happy ending of every task: "Your card is frozen", "We'll call you within one working day".
Links out
None: the task is over
Settings
Last message (optional), and Mark the conversation resolved (main flow only). Inside a sub-flow, End returns to the step after Sub-flow instead.
›JSON
"call_booked": {
  "type": "end",
  "message": "Thanks! We'll call you within one working day.",
  "resolve": true
}

Which step do I need?

Start from what you want to happen.

I want the agent to…Use
Say something and carry onMessage
Offer a few fixed optionsButtons
Get a phone number, date, amount or reference, and check itAsk for
Double-check before changing anythingConfirm
Do different things depending on a rule or a limitCondition
Route on one value with many possibilitiesSwitch
Show something only the first timeOnce
Remember, tidy or forget a valueSet variables
Look something up or change something in my systemAPI call
Make sure it's really the logged-in customerVerified only
Tell my team about something to do after the chatNotify by email
Count how often a task succeedsMark goal
Answer in its own words from my documents (hybrid agents only)AI answer
Jump back to the menu or into another taskGo to
Reuse the same steps in several placesSub-flow
Bring in a personHand off
Wrap up a taskEnd

A common shape for a task that changes something: Verified only API call Ask for Confirm API call Mark goal End, with every Error, Gave up and No answer linked to a Hand off.

Covers the 17 step types in the flow builder's Canvas and JSON (advanced) views (AI answer: hybrid agents only). Box colours match the canvas.