Build a conditional conversation flow¶
Goal: build a two-stage onboarding flow where the second stage is skipped if the data it would collect already exists in memory. We'll walk it through the portal's conversation editor end-to-end, from a fresh agent to a working conditional bypass.
For the design rationale of when to use deterministic edges vs. LLM-controlled routing, read Edge conditions first. For the broader routing model, see Conversation routing.
What we're building¶
An onboarding conversation with two stages:
collect-name— asks for the user's name and stores it inmemory.name.collect-business-type— asks for industry and stores it inmemory.business_type. Conditional — only entered ifmemory.business_typeis missing.
When a returning user comes back, memory.name and
memory.business_type may already be set from a prior session.
The flow should glide through stages whose data is already on file
and stop only at stages with missing data.
Prerequisites¶
- An agent with a chatbot system memory and an LLM provider configured on the Settings tab.
- At least one conversation node already created. If you don't have one, see Building a chatbot agent first.
- Org admin role on the agent's organization (the conversation editor saves through admin-gated mutations).
Step 1: Open the conversation editor¶
- Open the agent detail page → Chatbot Control tab.
- In the conversation list, click into the conversation you'll add the flow to (or create a new one with Add conversation).
- The editor opens with the conversation's stages on the left and stage details on the right.
If your conversation has only the auto-generated welcome stage, that's fine — we'll add two more.
Step 2: Add the two stages¶
For each stage, click Add stage and fill in:
collect-name
- Name:
collect-name - Prompt:
What's your name? - Extraction spec: a single field —
memory.name(string, description "the user's name").
collect-business-type
- Name:
collect-business-type - Prompt:
What kind of business are you in? - Extraction spec:
memory.business_type(string).
Save both. The two stages now exist; what they don't have yet are the edges between them.
Step 3: Wire the linear flow¶
Add an edge from collect-name to collect-business-type using
the Next preset:
- Select the
collect-namestage. - Click Add edge.
- Target:
collect-business-type. - Preset: Next — the editor fills in
timing: on_completeandbehavior: transition. Leave the condition empty (always fires). - Label: "Move on after collecting name" (any text — used in the editor view, not at runtime).
- Save.
A user driving the bot now flows from collect-name →
collect-business-type automatically when the first stage
completes.
Step 4: Add the conditional bypass¶
Now the conditional part. We want collect-business-type to be
bypassed if memory.business_type is already set. The
cleanest way is a Prerequisite-style edge from
collect-business-type back to itself's next stage when the
data is already present — but since we only have two stages, what
we really want is to skip the stage on entry: detour to wherever
"after onboarding" lives, or to the conversation's terminal stage.
For this walk-through, assume a third stage welcome-back exists
(or replace it with whatever stage the bot would proceed to after
onboarding). Add this edge on collect-business-type:
- Select
collect-business-type. - Click Add edge.
- Target:
welcome-back(or your post-onboarding stage). - Preset: Prerequisite-skip if your editor offers it; if not, set the fields manually:
- Timing:
on_enter - Behavior:
transition - Condition: build it with the Condition Builder:
- Click Add clause.
- Field:
memory.business_type. - Operator:
exists(the editor serialises this to{ "!": { "missing": ["memory.business_type"] } }— JSONLogic for "the field is present").
- Priority:
5(lower priorities fire first;5puts this ahead of any defaultpriority: 10follow-on edge). - Label: "Skip if business type is already set."
- Save.
The editor renders the condition as a tidy chip
(memory.business_type · exists); behind the scenes it stores the
JSONLogic expression you saw above.
Step 5: Test the flow¶
The fastest way to test conditional flows is the agent's Chat tab.
First-run path (data missing)¶
- Open the Chat tab on the agent. Click New chat.
- The bot greets and asks for your name.
- Reply with a name. The bot transitions to
collect-business-type(the Next edge from Step 3 fires). - Reply with an industry. The bot moves on.
Confirm in the route history that both stages were entered.
Returning-user path (data present)¶
- Without leaving the Chat tab, open a second tab to the agent's
Memory tab and confirm
memory.business_typeis set. - Back in Chat, click New chat.
- The bot greets and asks for your name.
- Reply with a name. The bot transitions toward
collect-business-type— and immediately fires the Prerequisite-skip edge, jumping past it towelcome-back.
You can confirm the skip in two places:
- Route history (
h-chat-get-route-historyor the portal's Chat tab debug panel) showscollect-business-type → welcome-backwith the trigger labelled as the conditional edge. - Stage toast in the chat surface flashes the skip — there's
no user-visible question for
collect-business-type.
Common issues¶
| Symptom | What to check |
|---|---|
| The skip never fires; the bot still asks for business type. | The Prerequisite-skip edge's timing is on_complete instead of on_enter. With on_complete it fires after the prompt has run — too late. Switch to on_enter. |
| The skip fires every time, even on first run. | The condition reads missing instead of exists. With missing, an empty memory satisfies the condition and the edge fires. Flip the operator. |
| The skip fires but the bot asks the question anyway. | The destination stage has its own Prerequisite edge pointing back to collect-business-type. Walk the route history forward — at some point the loop pops back. |
| Editor shows a "raw JSON view" instead of the condition builder. | Someone hand-edited the JSONLogic and used an operator outside the curated UI subset (e.g. >, arithmetic, nested operators). Either edit the JSON directly or simplify. |
The condition saves, but at runtime the engine fails with EDGE_CONDITION_FAILED. |
The condition references a variable scope outside memory.* / chat.* / agent.* / message.data.*. Check the Variable picker for the supported scopes. |
What you can build from here¶
The Prerequisite-skip pattern in this how-to generalises:
- Skip the welcome turn for returning users:
chat.message_count > 0skip from welcome to first real stage. - Branch by user type: a single
memory.user_type === "founder"clause picks one of two stage targets via two edges with mirror conditions. - Time-bounded conversations: a
now() > "2026-12-31T00:00:00Z"condition to retire a seasonal flow without deleting it.
Each stays in the same model — JSONLogic, scoped variables, edges with timing and behavior. The portal authors the curated subset; the engine evaluates the full grammar. See Edge conditions for the asymmetry and what's deferred.
Related¶
- Edge conditions — when to reach for deterministic conditions vs. LLM routing.
- Conversation routing — topics / conversations / stages / goals model.
- Building a chatbot agent — agent setup this how-to assumes is already done.