> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstflow.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Authoring

> Build experiences as flow graphs by hand, by AI, or imported from a URL then add branching, side-effects, and theming.

Authoring is how you turn an idea ("welcome new trial users", "ask churned
users why they left", "offer help when someone seems stuck") into a running
[experience](/concepts/experiences). Every experience whether a guide, tour,
survey, or announcement is the same underlying structure: a **flow graph** of
`{ nodes, edges }`. You assemble that graph in the visual editor, generate it from
a plain-language prompt, or extract it from an existing URL, then layer in
branching and side-effects. This page is the map of what the editor gives you and
how each capability behaves at runtime.

The mechanism matters because it explains the limits. The graph you draw is the
**authoring** graph, stored as the `flow` field (React Flow `{ nodes, edges,
viewport }`, including editor chrome). On every save the backend derives a second,
normalized **runtime** graph `flowRuntime` with chrome stripped and edges
validated. You only ever edit `flow`; the backend walks `flowRuntime`. At runtime,
once a user is [eligible](/features/targeting), the backend walks the runtime graph
edge by edge evaluating decisions, running actions, and (for guides) composing a
widget at the terminal node then pushes the finished result over Socket.IO. The
[browser SDK](/react/overview) is a passive consumer; nothing in authoring runs in
the browser.

```mermaid theme={null}
flowchart LR
  A([Author the flow<br/>visual / AI / import]) -->|save| B[flow JSON<br/>nodes + edges + chrome]
  B -->|normalize on save| C[flowRuntime<br/>chrome stripped, edges validated]
  C -.->|eligible user| D{Backend walks<br/>flowRuntime}
  D -->|branch| E[Classify / Decision]
  D -->|side-effect| F[API Call / Webhook / Slack]
  D -->|guide result| G[Compose WidgetTree<br/>Anthropic claude-sonnet-4-6]
  G -->|Socket.IO push| H([Browser renders widget])
  style A fill:#e8f0fe,stroke:#3b5bdb,color:#1a1a1a
  style G fill:#fff3bf,stroke:#f08c00,color:#1a1a1a
  style H fill:#d3f9d8,stroke:#2f9e44,color:#1a1a1a
  style D fill:#f3f0ff,stroke:#7048e8,color:#1a1a1a
```

## Build the flow

Three entry points produce the same `flow` JSON. Pick whichever gets you to a
working draft fastest, then refine in the editor they are not mutually
exclusive.

The **visual flow editor** is the canonical surface: drag nodes onto the canvas,
connect them with edges, and configure each node in its side panel. Which node
types appear is governed by the experience `type` a survey exposes question
nodes, a tour exposes message/card nodes, a guide exposes a single Result node.
See [Flows & nodes](/concepts/flows-and-nodes) for the full node registry.

**AI generation** turns a plain-language description into a complete flow,
streamed live as the graph builds. Describe the outcome ("a 3-question NPS survey
that thanks promoters and asks detractors what went wrong") and FirstFlow lays out
the nodes and edges for you. It is the fastest way to a first draft of a multi-step
[experience](/concepts/experiences#authoring-options); you then tune wording,
branching, and targeting by hand.

**Import from URL** points at an existing help article or feature page and extracts
its content into a flow useful when you already have written documentation and
want it delivered in-chat as a guide rather than rewritten from scratch.

After a flow exists, the **AI chat assistant** edits it conversationally: ask for a
change ("add a follow-up question for detractors", "make the CTA say 'Start free
trial'") and the assistant rewrites the graph in place, so you can iterate without
hand-wiring every edge.

## Branch the flow

Branching is what makes an experience adaptive instead of linear. Two node types
split the graph, and they differ in how the branch is chosen.

A **classify** node branches with an LLM. You configure the provider and model,
custom instructions, a **confidence floor**, which signals the model sees (the
latest message, a conversation digest, metrics), a **resolution mode** (blocking
waits for the verdict; optimistic proceeds and corrects), a TTL, and a **fallback
branch** taken when confidence is below the floor or the call errors. Use it for
intent-driven routing "send users asking about billing down the upgrade path".

A **decision** node branches with rules (legacy; hidden by default). Each branch is
a set of conditions tested against `traits.*` and `answers.*`; conditions combine
with **AND within a branch and OR across branches**, and the first matching branch
wins. The operator vocabulary is shared with targeting see the
[condition operators reference](/concepts/triggers-and-audience#condition-operators).

Both branch types are evaluated **server-side** as the backend walks the runtime
graph, so the browser never sees the branching logic or the data it tests against.

## Run side-effects mid-flow

Action nodes let a flow do something in the outside world before it reaches its
result fetch live data, notify a system, or message a channel. All run on the
backend, so endpoints and credentials are never exposed to the browser.

<Steps>
  <Step title="Fetch live data with an API Call node">
    An [API Call node](/integrations/api-calls) performs a server-side HTTP fetch
    mid-flow and feeds the response into the flow context. Downstream nodes —
    including a guide's widget composition can reference that data, so a widget
    can show the user's real plan limits or order status.
  </Step>

  <Step title="Notify your systems with a Webhook node">
    A [Webhook node](/integrations/webhooks) sends an HTTP request to your endpoint
    when the flow reaches it. Delivery is backed by an outbox table with a retry
    worker and exponential backoff, so a transient failure on your side does not
    drop the event.
  </Step>

  <Step title="Post to a channel with a Slack message node">
    A [Slack node](/integrations/slack) posts a message or DM for example,
    alerting a sales channel when a high-value account triggers an experience.
  </Step>
</Steps>

A common pattern is to chain an API Call into a branch: fetch account status, then
route with a decision or classify node so trial accounts and paid accounts see
different guides. See [API calls](/integrations/api-calls#use-it-with-branching).

## Compose the widget (guides)

For [guide](/concepts/experiences) experiences the flow has no authored UI its
terminal **Result** node holds an *instruction*, not a layout. When the walk
reaches it, the backend composes a [`WidgetTree`](/concepts/widgets-and-blocks)
from the conversation context using Anthropic (`claude-sonnet-4-6`), then pushes
that tree over Socket.IO for the browser to render. This is why a guide's output
adapts to the conversation while a tour's stays fixed: the guide is composed
fresh at runtime, the tour is authored block-by-block.

Tours, surveys, and announcements are the opposite end you author each step
explicitly with message, card, and question nodes, and the runtime renders exactly
what you built. The block primitives those steps render into are documented in
[Widgets & blocks](/concepts/widgets-and-blocks).

## Theme and preview

Style the widget with light/dark design tokens from the agent's **Appearance**
page; tokens are delivered over the realtime connection and applied with no code.
You can override them per instance in code via the `theme` prop on
[`FirstflowWidget`](/react/widget) see [Theming](/react/theming) for the full
token list.

Before publishing, test the experience in the **preview simulator**: it runs the
flow in a simulated chat so you can step through branches, answer questions, and
see the composed widget without affecting real users or analytics.

<Note>
  Eligibility which users see an experience and when is configured separately
  from the flow, in the experience's [triggers and audience](/features/targeting)
  settings, and is evaluated server-side. Authoring decides *what* the experience
  is; targeting decides *who* gets it and *when*.
</Note>

## Next

Set who sees each experience in [Triggers & audience](/features/targeting), or read
the full node registry in [Flows & nodes](/concepts/flows-and-nodes).
