Skip to content

Wizards overview

A wizard is a multi-step, guided data-entry flow. Where a form captures one record on one screen, a wizard walks the user through a graph of steps — collect some fields, branch on what they answered, run a bit of SQL, then finish — and can hand the result off to a workflow for review and approval.

Wizards are authored in *.wizard.xml as a node graph, edited visually in the in-app Wizard Designer, and run by the React Wizard Runner at /wizard/{slug}.

Reach for a plain view/form when the user edits one record on one screen. Reach for a wizard when the capture is inherently sequential or conditional:

  • the input spans several logical steps and you want a progress bar + review screen;
  • later steps depend on earlier answers (a Decision branch);
  • you need to run intermediate SQL between steps (create a draft row, look something up);
  • submission should start an approval workflow rather than just write a row.

A wizard is a set of nodes connected by links (directed edges). The runner starts at the entry node and follows links, pausing only where it needs the user. There are five node types:

Node Purpose Runner behaviour
Startup Initialise global variables. Auto-advances immediately.
Input Collect user data via fields. Renders a step and waits for Next.
Decision Branch on the collected values. Auto-advances down the first matching link.
Action Side effect: alert, run SQL, or set a variable. Auto-advances (an Alert pauses for acknowledgement).
Terminal Finish, and optionally hand off to a workflow. Submits the response and shows the outcome.

Only Input and Terminal (and an Alert action) stop the runner; Startup, Decision and the other Actions execute silently in an “auto-advance burst” between visible steps.

Everything the wizard knows is a variable. Input fields become variables (keyed internally as NodeId__FieldName, with a bare FieldName alias), Startup declares named globals, and an Action can compute new ones. Variables flow into:

  • Decision link expressions (which branch to take);
  • SQL blocks, as @FieldName / @VariableName substitutions (plus session parameters like @SessionUserId and @SessionCompanyId);
  • the saved response, and the workflow context on handoff.

<Link From="a" To="b" /> is an edge. A Decision node’s outgoing links carry an Expression; the first whose expression is true wins, with Expression="else" (or an expression-less link) as the catch-all. Expressions use the same expression engine as views.

The most powerful thing a wizard does is finish into a workflow. When a Terminal node declares a WorkflowName, submitting the wizard:

  1. saves a WizardFormResponse row (the collected values as JSON);
  2. optionally runs the Terminal’s SQL;
  3. starts the named workflow, bound to that response (EntityType = "WizardFormResponse"), passing the wizard’s variables as the initial workflow context.

From there the workflow handles approvals, decisions and downstream actions independently. Editing and resubmitting a response can instead continue the existing workflow instance via a resubmission transition — see Runtime & designer.

This is the shape of a wizard end to end — a Startup global, one Input step, an Action that creates a draft row, and a Terminal that starts an approval workflow:

┌───────────────────────────────┐
│ Startup · Channel global │
└───────────────┬───────────────┘
┌───────────────────────────────┐
│ Input · Product Details │
└───────────────┬───────────────┘
┌───────────────────────────────┐
│ Action · Create Draft (SQL) │
└───────────────┬───────────────┘
┌───────────────────────────────┐
│ Terminal · Submitted │
└───────────────┬───────────────┘
▼ starts on submit
┌───────────────────────────────┐
│ Workflow · ProductApproval │
└───────────────────────────────┘
Prefer a picture?
<Wizard Name="NewProductRequest" Label="New Product Request" Slug="new-product-request" Schema="Inventory">
<!-- 1. Startup: seed a global, then auto-advance -->
<Node Id="start" Type="Startup" Label="Start" X="-60" Y="38" Width="160" Height="60">
<Variables>
<Variable Name="Channel" Type="String" Default="Wizard" />
</Variables>
</Node>
<!-- 2. Input: collect fields (one is a SQL-backed Select) -->
<Node Id="details" Type="Input" Label="Product Details" X="220" Y="-12" Width="200" Height="160">
<Fields>
<Field Name="ProductName" Type="String" Label="Product Name" Required="true" />
<Field Name="CategoryId" Type="Select" Label="Category" Required="true" ControlType="Select2">
<OptionsSql>SELECT Id AS Value, Name AS Label FROM Inventory.Categories WHERE IsActive = 1</OptionsSql>
</Field>
<Field Name="UnitPrice" Type="Number" Label="Unit Price" Required="true" />
</Fields>
</Node>
<!-- 3. Action: run SQL to create a draft, capture the new id into a variable -->
<Node Id="register" Type="Action" Label="Create Draft" X="480" Y="30" ActionType="SetVariable">
<TargetVariable>ProductId</TargetVariable>
<ValueSource>SQL</ValueSource>
<SQL>INSERT INTO Inventory.Products (Name, CategoryId, UnitPrice, Status) VALUES (@ProductName, @CategoryId, @UnitPrice, 'Draft'); SELECT CAST(SCOPE_IDENTITY() AS BIGINT);</SQL>
</Node>
<!-- 4. Terminal: finish and start the ProductApproval workflow -->
<Node Id="done" Type="Terminal" Label="Submitted" X="740" Y="38"
SubmissionStatus="Pending" WorkflowName="ProductApproval" ResubmissionTransition="Resubmitted">
<SuccessMessage>Your product request has been submitted for approval.</SuccessMessage>
</Node>
<Link From="start" To="details" />
<Link From="details" To="register" />
<Link From="register" To="done" />
</Wizard>

The full reference (every node type, field type, SQL block and attribute) is in Authoring a wizard. How it executes and how the designer/runner work is in Runtime & designer.