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

# Customer Flows

> Define the customer conversations your agents should handle, and get graded against

<Info>
  **Prefer to keep this in your repo?** Customer flows can be defined as [config as code](/documentation/config-as-code/flows) (YAML in git, applied with the [CLI](/documentation/sdks/cli)) instead of built in the dashboard.
</Info>

## Quickstart

Create a customer flow (an improv brief graded against your expectations):

<Tabs>
  <Tab title="Node">
    ```typescript theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
    const flow = await client.customerFlow.create({
      type: 'IMPROV',
      title: 'Billing questions',
      agentIds: ['<agent-id>'],
      happyPath: {
        title: 'Asks about a charge',
        personaOverrideId: '<persona-id>', // a built-in or custom persona
        environmentId: '<environment-id>', // e.g. "Quiet line"
        prompt: 'You want to understand a charge on your latest invoice.',
      },
      agentExpectations: [{ prompt: 'Agent explains the charge clearly' }],
    })
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
    flow = client.customer_flow.create(
        type="IMPROV",
        title="Billing questions",
        agent_ids=["<agent-id>"],
        happy_path={
            "title": "Asks about a charge",
            "personaOverrideId": "<persona-id>",
            "environmentId": "<environment-id>",
            "prompt": "You want to understand a charge on your latest invoice.",
        },
        agent_expectations=[{"prompt": "Agent explains the charge clearly"}],
    )
    ```
  </Tab>

  <Tab title="Config as Code">
    ```yaml roark/flows/billing-questions.yaml theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
    kind: flow
    type: improv
    name: billing-questions
    agents: [frontdesk]
    happyPath:
      persona: frustrated-caller
      environment: Quiet line
      prompt: You want to understand a charge on your latest invoice.
    expectations:
      - Agent explains the charge clearly
    ```

    ```bash theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
    roark config apply ./roark
    ```
  </Tab>
</Tabs>

Need ids? Grab a built-in [persona](/documentation/simulation-testing/personas) and environment with `simulationPersona.list()` / `simulationEnvironment.list()` (see the [first-simulation quickstart](/documentation/getting-started/introduction#your-first-simulation)). Both are required on the happy path.

## What is a customer flow?

A customer flow describes one type of customer conversation: who the customer is, what they want, the variants you need to cover (language, mood, environment), and the **agent expectations** the agent is graded against. Flows live under **Simulate → Customer flows** in the sidebar.

Today, flows drive simulations: you attach them to run plans and Roark generates test calls from them. The same definition is what live-call analytics will grade incoming traffic against next.

<Note>
  Customer flows supersede the "scenarios" concept from earlier versions of Roark. The REST API and SDKs still use the older name. For example, run requests take a `scenarios` field.
</Note>

Every flow has:

| Part                    | What it does                                                                     |
| :---------------------- | :------------------------------------------------------------------------------- |
| **Title & description** | Identify the flow in the hub and in run reports                                  |
| **Linked agent(s)**     | At least one agent must be linked: saving is blocked otherwise                   |
| **Labels**              | System labels (Adversarial, Health check, Voicemail) and free-form custom labels |
| **Agent expectations**  | LLM-graded instructions, inherited by every variant                              |
| **Variants**            | One default **happy path** plus any number of **edge cases**                     |

***

## Authoring modes: Improv vs Scripted

Each flow is authored in one of two modes, toggled with the **Improv / Scripted** pill in the editor header.

|           | **Improv**                                                                                    | **Scripted**                                                                                                                                                                       |
| :-------- | :-------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| You write | A free-text customer brief: the situation the customer is in, what they want, how they behave | The conversation itself, step by step, on a graph canvas                                                                                                                           |
| Each run  | The simulator improvises a fresh conversation: different words, same intent                   | Walks your graph, following the exact lines you authored. [Branching](#branching-how-paths-become-calls) decides whether every path runs as its own call or one call branches live |
| Best for  | Realistic, open-ended conversations; broad behavioral coverage                                | IVR menus, DTMF entry, deterministic routing, precise turn-by-turn checks                                                                                                          |
| Variants  | You author them by hand                                                                       | Derived automatically: one per path through the graph                                                                                                                              |

Switching modes is non-destructive: both data shapes coexist on the flow, so you can toggle back and forth. If the other mode already has authored content, the editor asks you to confirm the switch. Your work is retained either way.

<Note>
  In the GraphQL API, Improv mode is the `UNSCRIPTED` value of the `FlowMode` enum. There is no `IMPROV` enum value. The UI label is newer than the API name.
</Note>

### Improv mode

The improv editor is organized into three numbered sections:

1. **Happy path**, the customer setup: a free-text brief describing the situation the customer is in, what they want, and how they typically behave. This section also carries the happy path's Persona, Environment, and Variables chips, and a **Link preceding flow** control (see [flow composition](#composing-flows) below).
2. **Agent expectations**: the required agent picker plus expectation rows, inherited by every variant.
3. **Edge cases**: additional variants that inherit from the happy path and override only what differs.

### Scripted mode

Scripted mode replaces the form with a full-screen canvas where you author the conversation as a graph of steps. See [the scripted graph](#the-scripted-graph) for step kinds and rules.

***

## Variants: happy path and edge cases

A variant is one per-caller configuration of the flow: a title, persona, environment, variables, per-variant labels, and optional additional expectations.

* **Exactly one variant is the default**: the **happy path**. It must have a customer setup and a persona.
* **Every other variant is an edge case.** Edge cases inherit the happy path's setup, persona, and environment unless they override them. The setup field on an edge case reads "Inherited from the happy path, type to override."
* **Additional expectations append.** An edge case can add expectations that apply just to that variant; they never replace the flow-level set.
* Each edge case can carry its **own labels**, and its menu lets you **Mark as happy path** (making it the new default) or **Delete edge case**.

<Tip>
  An edge case that overrides nothing is pointless, and the editor blocks saving it. Give each edge case at least a different setup, persona, or environment.
</Tip>

### Persona, Environment, and Variables chips

Every variant carries a chip strip configuring who's calling and from where:

* **Persona**: the simulated customer's voice and delivery. The happy path requires one (a new flow auto-seeds the built-in **Polite First-Time Caller** persona, falling back to your project's first persona); the picker can also create or edit personas inline. See [Personas](/documentation/simulation-testing/personas).
* **Environment**: the background noise played under the customer's voice and how loud it plays. Pick a Roark preset (Quiet line, Office, Coffee shop, City street, Driving, Airport, Children playing, Thunderstorm), or drag the noise level and save the result as one of your own environments. Edge cases also get an **Inherit** option. See [Environments](/documentation/simulation-testing/environments).
* **Variables**: per-variant key/value entries (name, account ID, last visit…) handed to the customer-side model so it can answer the agent's questions consistently. Add rows manually, or use **Apply a test profile** to replace the entries with a saved profile's properties. Prompt and step text support `{{variable}}` tokens with autocomplete: `{{name}}` for flow-scoped properties, `{{persona.name}}` for persona-scoped ones. See [Variables](/documentation/simulation-testing/variables).

***

## Agent expectations

Agent expectations are the graded contract of the flow: short, LLM-evaluated instructions like *"Agent greets warmly and uses the caller's name once."* After each simulated conversation, Roark checks the transcript against every expectation and reports pass/fail per expectation, per call.

* **Flow-level expectations** are inherited by every variant.
* **Additional expectations** on an edge case apply only to that variant, on top of the flow-level set.
* In scripted flows, expectations are also derived from the agent turns along each path: what you wrote the agent should say becomes what it's graded on.

Write expectations as specific, checkable statements. "Agent offers a callback when the customer declines to hold" grades cleanly; "Agent is helpful" doesn't.

***

## Labels

Flows use a single label system with two kinds:

* **System labels**. Roark-curated: `Adversarial`, `Health check`, `Voicemail`. These drive template behavior; for example, the Red teaming template automatically runs flows carrying the Adversarial label. Roark also maintains a `Happy path` label automatically on each flow's default variant, which templates that run happy paths source; you don't apply it by hand.
* **Custom labels**: free-form tags for your own organization.

Labels attach at the flow level and per edge case, and the hub filters by them (alongside a **Mode** filter for Improv vs Scripted).

***

## The scripted graph

In scripted mode you build the conversation as a directed graph rooted at a start node. Click **Add step** to insert a step, then connect steps with edges; selecting a node opens an inspect panel with its kind-specific fields.

### Step kinds

| Step              | What it does                                                                      |
| :---------------- | :-------------------------------------------------------------------------------- |
| **Agent**         | What the agent says at this point (also becomes a graded expectation on the path) |
| **Customer**      | What the customer says back                                                       |
| **First message** | The customer's opening line (only valid immediately after the start node)         |
| **DTMF**          | The caller presses keypad digits (`0–9`, `*`, `#`; use `#` to mark end-of-input)  |
| **Silence**       | The caller pauses for 1–60 seconds                                                |
| **Flow link**     | Splices another scripted flow's steps in at this point                            |

<Note>
  The API keeps older names here too: step kinds map to the `SimulationStepType` enum, where a Flow link is `SCENARIO_LINK` and a First message is `CUSTOMER_FIRST_MESSAGE`.
</Note>

<Note>
  **Looking for the Voicemail step?** Voicemail is a run template now, not a step. See [Voicemail Testing](/documentation/simulation-testing/voicemail). A voicemail has no conversation to graph and only one meaningful variable (which greeting plays), so the template runs Roark's recordings directly. Flows that already contain a Voicemail step keep working; you just can't add new ones.
</Note>

### Graph rules

* **Branching is allowed**: give a step multiple outgoing edges to fork the conversation.
* **Merges are allowed**: branches can rejoin.
* **Cycles are blocked**: the graph must be a DAG, so a conversation always terminates.

### Paths derive variants

Every unique route through the graph from start to finish is a **path**, and when you save the flow, Roark derives one variant per path. Each path card in the right rail shows a generated description of its route. You don't hand-author scripted variants; you shape the graph and the variants follow.

You can still control *who* runs each path: **Add variant for path** creates an extra copy of a path that runs it with a different persona, environment, or variables. The rail marks the flow's default variant with a **Default** badge and groups the rest under **Edge cases**, with **Make default** and **Delete variant** available per variant.

How those paths turn into *calls* is a separate choice, covered next in [Branching](#branching-how-paths-become-calls).

### Branching: how paths become calls

A branching graph can be run two ways, chosen with the **Branching** control at the top of the variant rail. Both modes speak the exact lines you authored. The choice decides only *when* a branch is picked and *how many calls* a run places. Neither mode changes how metrics or agent expectations grade.

|                  | **Simulate every path**                                     | **Adapt to your agent**                                                       |
| :--------------- | :---------------------------------------------------------- | :---------------------------------------------------------------------------- |
| Branch is picked | Up front, when the flow is saved                            | During the call, from what your agent actually says                           |
| Calls per run    | One per variant the run includes                            | One per persona among the included variants                                   |
| Coverage         | Every included path, every run                              | One path per call, whichever the agent leads to                               |
| Best for         | Regression suites, full branch coverage, DTMF and IVR entry | Agents whose replies you can't predict; shared blocks linked into other flows |

Take a caller asking for a Tuesday appointment, where the agent's own answer forks the conversation:

```mermaid theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
graph TD
    C1["👤 Hi, can I book a\n cleaning for Tuesday?"] --> A1["🤖 Tuesday at\n 9am is open"]
    C1 --> A2["🤖 Tuesday is\n fully booked"]
    A1 --> C2["👤 Perfect,\n book it"]
    A2 --> C3["👤 What about\n Wednesday?"]
```

**Simulate every path** places two calls. Call 1 always takes the left branch and call 2 always takes the right one, even when your agent says something the other branch was written for. That is the point: a path that stopped matching your agent's behavior surfaces as a failure instead of being quietly routed around.

**Adapt to your agent** places one call. The simulated customer hears which reply your agent actually gave and follows that branch; the branch it didn't take isn't exercised on that call.

The **Branching** picker in the app shows the call count each mode places for the flow you're editing, with every variant running once. A run plan's own configuration wins over that figure: selecting a subset of variants, applying a persona override, or setting an iteration count all change what a run actually places.

<Note>
  **Personas multiply calls in both modes.** Adapt to your agent collapses a flow's paths into one call **per persona**, not one call outright. Five paths across three personas run as three calls. On a collapsed call, variable values come from the primary variant; the sibling variants contribute their path shape, not their own values. Sibling-only variable *names* are still detected and prompted for, so they don't vanish silently.
</Note>

<Warning>
  **Linking a multi-path flow requires Adapt to your agent.** A **Flow link** step targeting a flow that is set to *Simulate every path* and has more than one reachable path fails the run: composed fan-out across linked flows isn't supported yet. Either set the linked flow to **Adapt to your agent**, so the run follows whichever path the agent leads to, or link a single-path flow.
</Warning>

<Tip>
  Reach for **Adapt to your agent** when you genuinely can't predict the branch: an IVR tree whose menu order varies, or a shared block linked into many flows. Use **Simulate every path** for everything else. It's the mode that gives you coverage you can regress against.
</Tip>

<Note>
  **Adapt to your agent is not looser grading, and it is not goal-level authoring.** Both branching modes speak your exact scripted lines. To have the agent checked against intent ("confirms the caller's name") rather than a script, use an [Improv flow](#authoring-modes-improv-vs-scripted) with agent expectations.
</Note>

<Note>
  In the GraphQL API and the config-as-code DSL these are the `DETERMINISTIC` and `ADAPTIVE` values of the `ScriptedBranchingMode` enum. The UI labels are newer than the API names.
</Note>

<Warning>
  Structural edits (adding or deleting nodes or edges) freeze variant editing until you save: "You've edited the graph. Save the flow to refresh its paths before adding or removing variants."
</Warning>

***

## Composing flows

Flows compose in two directions:

* **Flow link (scripted → scripted).** A scripted flow can include a **Flow link** step that splices another scripted flow's steps in mid-graph. Use this to reuse a shared segment (an IVR menu, an identity-verification exchange) across many flows. Only scripted flows can be linked this way. A linked flow with more than one path must be set to **Adapt to your agent**; see [Branching](#branching-how-paths-become-calls).
* **Preceded by (scripted → improv).** An improv variant's **Link preceding flow** control names a scripted flow (and optionally a specific variant of it) that runs *before* the improv segment. Typical use: navigate a scripted IVR tree deterministically, then improvise the conversation once a human-like agent picks up.

Improv has no deterministic end, so an improv segment can only ever come last. You can't link *into* an improv flow from a scripted step.

***

## Creating a flow

From the hub, click **New flow** to open the chooser at `/customer-flows/new`. You can generate a draft or start blank:

**Generate: from existing material** (each opens the Ask Roark assistant with a seeded prompt):

* **Describe what you want**: tell Ask Roark the conversation in plain language and it drafts the flow.
* **From a transcript**: upload or paste a transcript and Roark turns it into a flow.
* **From your calls**: pick real calls from your production traffic to base the flow on.

**Or start blank:**

* **Improv: Describe the customer** opens the brief editor.
* **Scripted: Author the conversation** opens the graph canvas.

Variants drafted by Ask Roark carry a **Generated** badge until you review or promote them.

<Steps>
  <Step title="Title and describe the flow">
    Give it a clear title and a short description of the conversation type it covers.
  </Step>

  <Step title="Write the happy path">
    In Improv, write the customer setup brief; in Scripted, build the graph. Pin a persona, pick an environment, and add any variables the customer should know.
  </Step>

  <Step title="Link an agent and add expectations">
    Pick at least one agent, then add expectation rows describing what the agent must do. These are inherited by every variant.
  </Step>

  <Step title="Add edge cases">
    Cover the deviations that matter: a different language, a frustrated caller, a noisy environment, missing information. Override only what differs from the happy path.
  </Step>

  <Step title="Save">
    The save button surfaces the first blocking issue if anything is missing, for example "Link at least one agent to save", "The happy path needs a prompt", or (scripted) "Add at least one step to the graph".
  </Step>
</Steps>

***

## Using flows in a run

Flows drive simulations through run plans. In the create-run page, the **Attach flows** picker lists your library; expand a flow to choose its variants. Each attachment carries:

* **A variant selection**: all variants, the default variant (happy path) only, or specific variants you pick.
* **An optional persona override**: run the flow as a different customer without editing it. The Multilingual template uses this to fan one flow out across language personas.
* **Variable overrides**: per-attachment `{{variable}}` values that win over the variant's stored values at run time.

When a run starts, it snapshots the attached flows and variants, so the report always reflects exactly what was tested, even if you edit the flow afterwards.

<CardGroup cols={2}>
  <Card title="Run Plans" icon="list-checks" href="/documentation/simulation-testing/run-plans">
    Combine flows, agents, and metrics into reusable test suites
  </Card>

  <Card title="Personas" icon="users" href="/documentation/simulation-testing/personas">
    Configure the simulated customer's voice and delivery
  </Card>

  <Card title="Variables" icon="braces" href="/documentation/simulation-testing/variables">
    Parameterize flows with per-variant and run-time values
  </Card>

  <Card title="Templates" icon="layout-template" href="/documentation/simulation-testing/templates">
    Start runs from goals like red teaming or multilingual coverage
  </Card>
</CardGroup>
