Skip to main content
A flow is a JSON object. The editor builds it, the backend stores and serves it, and the iOS SDK renders it. This page documents the exact shape so you can read an exported flow, bundle one offline, or debug a resolve response. The schema is the single source of truth for both the editor and the SDK. Its current content version is CURRENT_SCHEMA_VERSION = 8 (see Schema versions).
This is the published and exported shape. The SDK receives it as the flow_schema field of a resolve response. You normally never write this by hand; the editor generates it. Read it when you export a flow, bundle one for offline use, or inspect what the SDK actually received.

Top level: FlowDefinition

The root object.
FieldTypeNotes
idstringFlow ID.
namestringDisplay name.
versionnumberThe flow’s content/migration version (1 to 8). Bumped by migrations on load.
appIdstring (optional)Owning app, for multi-app setups.
createdAtstringISO 8601 timestamp.
updatedAtstringISO 8601 timestamp.
nodesFlowNode[]The graph nodes. See Nodes.
edgesFlowEdge[]The graph connections. See Edges.
entryNodeIdstringID of the node where the flow starts.
chromeGlobalChrome (optional)Deprecated. Replaced by persistentUI. Kept only so old flows migrate.
persistentUIPersistentUI (optional)Navigation bar, footer, and overlay zones that wrap every screen. See Persistent UI.
variablesFlowVariable[] (optional)Flow state. See Variables.
globalStylesFlowGlobalStyles (optional)Theme tokens (colors, typography, spacing, radii, shadows).
settingsFlowSettings (optional)Default transitions, localization, analytics provider.
backgroundScreenBackground (optional)Flow-level default background, inherited by screens. See Backgrounds.
metadataRecord<string, any> (optional)Editor/analytics extras.

Nodes

nodes is a discriminated union keyed on kind. Every node extends a base:
FieldTypeNotes
idstringUnique within the flow.
namestringDisplay name.
kindFlowNodeKindOne of screen, condition, assign, api, abTest, event, subflow.
tagsstring[] (optional)Free-form tags.
metadataRecord<string, any> (optional)Editor extras.
Only screen produces UI. condition, assign, and abTest are logic nodes the SDK evaluates to decide the next node. The kinds api, event, and subflow are declared in the schema but are not supported by the current SDKs: they are skipped during rendering. The editor also has no UI to create non-screen nodes today (see Navigation and branching). For A/B testing, use experiments, not abTest nodes.

ScreenNode (kind: "screen")

The only node kind that renders UI. It holds a component tree.
FieldTypeNotes
screenType"standard" | "modal" | "bottomSheet" | "fullScreen" (optional)Screen presentation style.
layoutComponentNodeThe root component (a stack, the “Root Stack”).
propsobject (optional)Screen-level settings. See below.
enterTransitionTransitionConfig (optional)Per-screen enter transition override.
exitTransitionTransitionConfig (optional)Per-screen exit transition override.
chromeOverridesScreenChromeOverrides (optional)Deprecated. Use screenSettings.
screenSettingsScreenSettings (optional)Per-screen zone settings. See Zones.
timelineScreenTimelineEvent[] (optional)Cross-component sequencing. See Animations.
analytics{ screenKey?, autoTrackImpressions? } (optional)Per-screen analytics hints.
ScreenNode.props fields:
PropTypeNotes
backgroundScreenBackgroundLayered background for this screen.
backgroundInheritbooleanWhen true (or undefined), inherit the flow-level background.
backgroundColor, gradientlegacyKept for backward compatibility during migration.
paddingnumber or { top, right, bottom, left }Screen padding.
safeAreabooleanRespect device safe area.
statusBarStyle"light" | "dark" | "auto"Status bar appearance.
includeInProgressbooleanCount this screen in auto progress. Defaults to true.
hideChromebooleanDeprecated path; current flows use screenSettings.hideAllZones.
animationSpeednumberGlobal multiplier for all animations on the screen. Default 1.0, range 0.25 to 2.0.
particleEffectScreenParticleEffectAuto-play particle effect. See Particles.

ConditionNode (kind: "condition")

If/else branching. Evaluated by the SDK; produces no UI.
FieldTypeNotes
conditionFlowExpression (string)A safe-subset expression. See Conditions and expressions.
outputs{ trueLabel?, falseLabel? } (optional)Labels for the true/false edges.

AssignNode (kind: "assign")

Sets variables from expressions. Evaluated by the SDK; produces no UI.
FieldTypeNotes
assignments{ variableKey: string; expression: FlowExpression }[]Each entry writes one variable.

AbTestNode (kind: "abTest")

A schema-level split. Most A/B testing is done with experiments instead.
FieldTypeNotes
experimentKeystringStable ID for analytics.
variants{ id, label, weight (0-100), targetNodeId }[]The branches.

Declared but unsupported nodes

These exist in the type definitions but are not rendered or evaluated by the current iOS or Expo SDKs. Do not rely on them.
  • ApiNode (kind: "api"): a backend/external HTTP call (request, responseMapping, onError).
  • EventNode (kind: "event"): an event-driven entry point (eventType, eventKey).
  • SubflowNode (kind: "subflow"): reuse another flow (subflowId, inputMapping, outputMapping).

Edges (FlowEdge)

Connections between nodes. The editor keeps edges in sync with screen order, so a goNext action follows the default edge to the next screen.
FieldTypeNotes
idstringUnique within the flow.
fromNodeIdstringSource node.
toNodeIdstringDestination node.
kind"normal" | "conditionTrue" | "conditionFalse" | "error" | "event" | "abVariant" (optional)Edge role.
labelstring (optional)Display label.
guardFlowExpression (optional)Extra condition for multi-branch nodes.
transitionTransitionConfig (optional)Per-edge transition (highest priority in the cascade).
prioritynumber (optional)Ordering hint.
metadataRecord<string, any> (optional)Editor extras.

Components

Each ScreenNode.layout is a ComponentNode tree:
FieldTypeNotes
idstringUnique within the flow.
typeComponentTypeOne of the 14 kinds. See Component reference.
propsRecord<string, any> (optional)Untyped prop bag. Resolved per renderer.
childrenComponentNode[] (optional)For stacks and other containers.
bindings{ [propName]: FlowExpression } (optional)Bind props to expressions.
interactionsComponentInteraction[] (optional)Tap/change/appear handlers. See Actions reference.
metadataRecord<string, any> (optional)Editor extras.
Props are stored as an untyped Record<string, any>. The intended shape per component kind is documented in the Component reference.

Persistent UI

persistentUI holds three optional zones that wrap every screen. Full coverage is in Persistent UI zones. The shape:
FieldTypeNotes
navigationBarPersistentZone (optional)Pinned to the top.
footerPersistentZone (optional)Pinned to the bottom.
overlayOverlayZone (optional)Floating layer over content.
settings.persistDuringTransitionboolean (optional)Whether zones stay fixed during transitions. Default true.

Variables

variables is an array of FlowVariable. Full coverage is in Variables. The shape:
FieldTypeNotes
keystringStable key referenced everywhere.
labelstring (optional)Display name.
type"string" | "number" | "boolean" | "list"Data type.
listItemType"string" | "number" | "boolean" (optional)Required when type is list.
scope"global"v1 supports global only.
lifecycle"static" | "session"Persistence.
source{ kind: "constant"; value } | { kind: "sdk"; path }Where the value comes from.
writablebooleanWhether actions can change it.
defaultValueVariableValue (optional)Fallback when the source is unavailable.

Example

A minimal one-screen flow with a single text and a Continue button:
{
  "id": "flow_welcome",
  "name": "Welcome",
  "version": 8,
  "createdAt": "2026-01-10T12:00:00Z",
  "updatedAt": "2026-01-10T12:00:00Z",
  "entryNodeId": "screen_1",
  "nodes": [
    {
      "id": "screen_1",
      "name": "Welcome",
      "kind": "screen",
      "layout": {
        "id": "root",
        "type": "stack",
        "props": { "axis": "vertical", "spacing": 16, "justify": "center" },
        "children": [
          { "id": "title", "type": "text", "props": { "text": "Welcome to Acme", "fontSize": 28, "fontWeight": "700" } },
          {
            "id": "cta",
            "type": "button",
            "props": { "text": "Continue", "variant": "primary" },
            "interactions": [
              { "id": "i1", "event": "onPress", "actions": [{ "kind": "closeFlow" }] }
            ]
          }
        ]
      }
    }
  ],
  "edges": [],
  "variables": []
}

Notes

  • The SDK decodes leniently. An unknown ComponentType decodes to an internal unknown case and renders as nothing (or a placeholder in debug builds) rather than failing the whole screen. A single malformed child is skipped, not fatal.
  • chrome and chromeOverrides are deprecated. New flows use persistentUI and screenSettings.
  • The integer version here (the content/migration version, up to 8) is not the same as the wire-format schema_version the SDK checks. See Schema versions.