Skip to content

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:

  1. collect-name — asks for the user's name and stores it in memory.name.
  2. collect-business-type — asks for industry and stores it in memory.business_type. Conditional — only entered if memory.business_type is 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

  1. Open the agent detail page → Chatbot Control tab.
  2. In the conversation list, click into the conversation you'll add the flow to (or create a new one with Add conversation).
  3. 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:

  1. Select the collect-name stage.
  2. Click Add edge.
  3. Target: collect-business-type.
  4. Preset: Next — the editor fills in timing: on_complete and behavior: transition. Leave the condition empty (always fires).
  5. Label: "Move on after collecting name" (any text — used in the editor view, not at runtime).
  6. Save.

A user driving the bot now flows from collect-namecollect-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:

  1. Select collect-business-type.
  2. Click Add edge.
  3. Target: welcome-back (or your post-onboarding stage).
  4. Preset: Prerequisite-skip if your editor offers it; if not, set the fields manually:
  5. Timing: on_enter
  6. Behavior: transition
  7. 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").
  8. Priority: 5 (lower priorities fire first; 5 puts this ahead of any default priority: 10 follow-on edge).
  9. Label: "Skip if business type is already set."
  10. 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)

  1. Open the Chat tab on the agent. Click New chat.
  2. The bot greets and asks for your name.
  3. Reply with a name. The bot transitions to collect-business-type (the Next edge from Step 3 fires).
  4. Reply with an industry. The bot moves on.

Confirm in the route history that both stages were entered.

Returning-user path (data present)

  1. Without leaving the Chat tab, open a second tab to the agent's Memory tab and confirm memory.business_type is set.
  2. Back in Chat, click New chat.
  3. The bot greets and asks for your name.
  4. Reply with a name. The bot transitions toward collect-business-type — and immediately fires the Prerequisite-skip edge, jumping past it to welcome-back.

You can confirm the skip in two places:

  • Route history (h-chat-get-route-history or the portal's Chat tab debug panel) shows collect-business-type → welcome-back with 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 > 0 skip 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.