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.
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 instantlyThe 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 visitorThe 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 visitorA 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 visitorThe 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 instantlySignposts 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 instantlyA 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 instantlyThe 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 instantlyA 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 instantlyThe 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.
$.statusor$.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 instantlyShowing 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 instantlyThe 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 instantlyA 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 instantlyThe 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"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 instantlyA 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"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"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 on | Message |
| Offer a few fixed options | Buttons |
| Get a phone number, date, amount or reference, and check it | Ask for |
| Double-check before changing anything | Confirm |
| Do different things depending on a rule or a limit | Condition |
| Route on one value with many possibilities | Switch |
| Show something only the first time | Once |
| Remember, tidy or forget a value | Set variables |
| Look something up or change something in my system | API call |
| Make sure it's really the logged-in customer | Verified only |
| Tell my team about something to do after the chat | Notify by email |
| Count how often a task succeeds | Mark goal |
| Answer in its own words from my documents (hybrid agents only) | AI answer |
| Jump back to the menu or into another task | Go to |
| Reuse the same steps in several places | Sub-flow |
| Bring in a person | Hand off |
| Wrap up a task | End |
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.