Interview Flow (Graph Mode)

A study runs either a linear question list or an interview flow — a branching script that can route on what a participant said, capture values out of an answer and reuse them later, pull data from your own API mid-interview, and notify your systems when someone reaches a point in the conversation.

September 26, 2026

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_graph and 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 text
  • guidance — private steering for the interviewer; never read aloud
  • options_from_var — use an upstream captured list as this question's choices, so the options are built from what the participant already said
  • min_turns / max_turns / follow_up — how hard the interviewer probes
  • requires plus on_missing (skip or fallback) — what to do when a referenced answer is not there
  • context — extra background for this step only
  • translations — 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

  1. Questions → Design Interview Flow.
  2. Import from question list to start from the questions you already have, or drag step buttons onto the canvas.
  3. Connect steps left to right. Double-click the background to search step types; select a step to edit its settings.
  4. Save & run Flow check — saves and validates. Problems are listed and clicking one jumps to the step.
  5. 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_graph returns 422 with the same error list check_graph gives, 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_version gives you optimistic concurrency: pass the version you last read and the write is refused with 409 if someone edited in the meantime.
  • deactivate_graph returns 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_fetch retrieved and what each webhook sent; read with get_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