There are two ways to author a flow, and they share one compiler and one validator, so they cannot drift apart:
- Browser — the canvas editor at Questions → Design Interview Flow (
/edit-graph/) - API/MCP —
set_graph,patch_graphand friends
The dashboard labels the feature "premium". In production today it is reachable on every plan, including Hobby.
Step types
| Step | Sits on the path? | What it does |
|---|---|---|
| Interview start | yes | Where every interview begins. Exactly one. |
| Question | yes | Asks something, optionally captures the answer |
Branch (decision, deterministic) |
yes | Routes on a condition |
Smart Branch (decision, llm_classifier) |
yes | Lets the model choose a path from labels you define |
| Interview end | yes | Ends the interview. At least one, reachable from everywhere. |
Capture from response (extract) |
no — attaches after a question |
Pulls specific values out of an answer |
Bring in data (context_fetch) |
no — attaches before a question |
Calls your API and makes the response available |
Send a signal (webhook) |
no — attaches after a question |
Notifies your system when participants reach this point |
The most common mistake is mixing the two kinds of connection. start, end, question and decision are joined by edges. extract and webhook attach with "after": "<question id>", context_fetch with "before": "<question id>" — they never appear in edges. The compiler splices them into the right place.
Question steps
Question types: open_ended, scale, options, multi_select, ranking, card_sort, slots, present_images, image_upload.
Useful fields beyond the obvious qtype and text:
output_var— capture the answer under a name, for later branches or piped textguidance— private steering for the interviewer; never read aloudoptions_from_var— use an upstream captured list as this question's choices, so the options are built from what the participant already saidmin_turns/max_turns/follow_up— how hard the interviewer probesrequirespluson_missing(skiporfallback) — what to do when a referenced answer is not therecontext— extra background for this step onlytranslations— per-locale overrides
Question text supports piped text: {{captured_answer}}, plus built-ins participant_id, survey_id, interview_id, visit, characteristics.<name>, metadata.<key> and trusted_metadata.<key>.
Branching
A Branch tests one condition: {"var": …, "op": …, "value": …}. Operators: eq, neq, in, contains, gt, gte, lt, lte, is_set, not_set.
Rules worth knowing before you draw:
- A step with more than one outgoing edge must be a decision.
- A decision needs exactly one edge marked
"default": true— unless its conditions enumerate every possible answer of a choices or scale question. - A condition is a single test. For and/or logic, chain two decisions.
- A captured answer can only be used by steps that always run after the step that produced it. Otherwise set
on_missing. - Loops are allowed, but every step in a loop needs a way out.
A Smart Branch replaces the condition with classes (the path labels) and instructions (how to choose). Use it when the routing depends on meaning rather than a stored value.
Side steps
Capture from response (extract) — instructions (what to pull out), output_var, output_type (string, list[string], number, object), merge for repeat visits (overwrite, append, append_unique), and on_empty.
Bring in data (context_fetch) — url (public http(s) only; internal addresses are rejected), method, body, headers, auth_header, timeout_s, on_error (skip continues, fail aborts), and either output_var for the whole response or outputs for named top-level keys.
Send a signal (webhook) — url, method, body, headers, auth_header, signal_type, and on_error (retry_then_skip or fail). Payloads are signed with a per-step secret, minted the first time the step is saved and stable across later saves; read it back with get_flow_signals {"study_id": "…"} and verify every delivery against it. These are separate from the organization-level webhooks that fire on lifecycle events like interview.completed.
In the browser
- Questions → Design Interview Flow.
- Import from question list to start from the questions you already have, or drag step buttons onto the canvas.
- Connect steps left to right. Double-click the background to search step types; select a step to edit its settings.
- Save & run Flow check — saves and validates. Problems are listed and clicking one jumps to the step.
- Activate Flow mode — switches the study from its question list to the flow.
The editor deliberately lets you save an invalid draft; it just will not let you activate one. The status line tells you where you stand ("Saved as draft — 1 flow-check issue(s)").
With MCP
describe_graph_nodes {} # read this first — the live schema
import_questions_to_graph {"study_id": "…"} # optional starting point
check_graph {"study_id": "…", "flow": {...}} # validate, saves nothing
set_graph {"study_id": "…", "flow": {...}, "expected_graph_version": 3}
patch_graph {"study_id": "…", "ops": [...]} # targeted edits
activate_graph {"study_id": "…"}
deactivate_graph {"study_id": "…"}
get_graph {"study_id": "…"}
- Nothing is saved while a flow has problems.
set_graphreturns 422 with the same error listcheck_graphgives, and the stored flow is untouched — a failed write can never leave a half-valid script. - Errors come back as
{node_id, code, message}using your own step ids. expected_graph_versiongives you optimistic concurrency: pass the version you last read and the write is refused with 409 if someone edited in the meantime.deactivate_graphreturns the study to its question list; the flow is kept, just unused.
What an interview leaves behind
A flow interview records more than its transcript:
- captured answers — the values your
output_vars captured - fetches and signals — what each
context_fetchretrieved and what eachwebhooksent; read withget_interview_fetches_and_signals - decision outcomes — how each branch routed
All of these are best-effort projections: if a write fails it is logged and the interview carries on.
Choosing between linear and flow
Use a question list when everyone should answer the same things in the same order — most studies. Use a flow when the interview needs to change shape: different paths for different segments, questions built from earlier answers, or data from your own systems mid-conversation.
Last updated: September 2026