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

# Example 3: Multi-Step Flow

> Chain multiple steps together to build a complete contact form workflow

Building on input collection, this example chains multiple steps together to create a complete contact form workflow.

***

## Objective

In this example, you'll learn:

* How to connect multiple steps using the `next` field
* How to persist data across steps with the `save` action
* The difference between transitional and terminal steps
* How workflow state evolves as steps complete

***

## The Scenario

You want to build a contact form that collects:

1. User's name
2. Email address
3. Preferred contact time

Each piece of information is collected in a separate step, and the data persists across the entire workflow.

***

## Implementation

Here's the complete tool definition:

```json theme={null}
{
  "type": "context",
  "context": {
    "task": {
      "type": "steps",
      "version": "v1alpha",
      "id": "contact-form",
      "tool": {
        "name": "submit_contact_info",
        "description": "Submit contact form information"
      },
      "steps": [
        {
          "id": "COLLECT_NAME",
          "goal": "Collect the user's name",
          "instructions": [
            "Ask the user for their name.",
            "Once you have their name, call the submit_contact_info tool with the user_name parameter."
          ],
          "inputs": [
            {
              "name": "user_name",
              "type": "string",
              "description": "The user's name",
              "required": true
            }
          ],
          "on": {
            "submit": [
              {"action": "save"}
            ]
          },
          "next": [{"id": "COLLECT_EMAIL"}]
        },
        {
          "id": "COLLECT_EMAIL",
          "goal": "Collect the user's email address",
          "instructions": [
            "Ask the user for their email address.",
            "Once you have their email, call the submit_contact_info tool with the user_email parameter."
          ],
          "inputs": [
            {
              "name": "user_email",
              "type": "string",
              "description": "The user's email address",
              "required": true
            }
          ],
          "on": {
            "submit": [
              {"action": "save"}
            ]
          },
          "next": [{"id": "COLLECT_TIME"}]
        },
        {
          "id": "COLLECT_TIME",
          "goal": "Collect the user's preferred contact time",
          "instructions": [
            "Ask the user for their preferred contact time.",
            "Once you have their preference, call the submit_contact_info tool with the contact_time parameter."
          ],
          "inputs": [
            {
              "name": "contact_time",
              "type": "string",
              "description": "The user's preferred contact time",
              "required": true
            }
          ],
          "on": {
            "submit": [
              {"action": "save"}
            ]
          }
        }
      ]
    }
  },
  "tool": {
    "type": "function",
    "function": {
      "name": "contact_form_workflow",
      "description": "Multi-step workflow with sequential input collection",
      "parameters": {
        "type": "object",
        "properties": {},
        "required": []
      }
    }
  }
}
```

***

## Key Concepts

### Step Transitions with `next`

The `next` field defines which step to go to after successful submission:

```json theme={null}
"next": [{"id": "COLLECT_EMAIL"}]
```

Key behaviors:

* **Evaluated after validation passes**: The workflow only transitions if all required inputs are present
* **Evaluated in order**: If multiple entries exist, the first matching one wins (more on this in Example 5)
* **Terminal if missing**: A step without `next` ends the workflow

### The `save` Action

By default, inputs are cleared when transitioning to a new step. The `save` action persists them:

```json theme={null}
"on": {
  "submit": [
    {"action": "save"}
  ]
}
```

**Without parameters**: Saves all step inputs to global variables with matching names.

**With specific inputs**:

```json theme={null}
{"action": "save", "inputs": ["user_name", "user_email"]}
```

**With a custom name**:

```json theme={null}
{"action": "save", "name": "contact_info.name", "inputs": ["user_name"]}
```

### Transitional vs Terminal Steps

| Step Type    | Has `next` | Behavior                            |
| ------------ | ---------- | ----------------------------------- |
| Transitional | Yes        | Moves to next step after submission |
| Terminal     | No         | Completes workflow after submission |

In this example:

* `COLLECT_NAME` → transitional (goes to COLLECT\_EMAIL)
* `COLLECT_EMAIL` → transitional (goes to COLLECT\_TIME)
* `COLLECT_TIME` → terminal (ends workflow)

### Dynamic Tool Schema Per Step

As the workflow progresses, the submit tool's schema changes:

**At COLLECT\_NAME:**

```json theme={null}
{
  "name": "submit_contact_info",
  "description": "Collect the user's name",
  "parameters": {"properties": {"user_name": {...}}}
}
```

**At COLLECT\_EMAIL:**

```json theme={null}
{
  "name": "submit_contact_info",
  "description": "Collect the user's email address",
  "parameters": {"properties": {"user_email": {...}}}
}
```

The agent always sees the correct schema for the current step.

***

## How It Works

Here's the conversation flow:

```
User: "Hello"

[Platform initializes workflow]
  → Current step: COLLECT_NAME

Agent: "Could you please tell me your name?"

User: "My name is Alice"

[Agent calls submit_contact_info(user_name="Alice")]
  → save action: user_name → global variable
  → next: transition to COLLECT_EMAIL
  → inputs cleared for new step

Agent: "Thank you, Alice! Now, could you please provide your email address?"

User: "alice@example.com"

[Agent calls submit_contact_info(user_email="alice@example.com")]
  → save action: user_email → global variable
  → next: transition to COLLECT_TIME

Agent: "Great! What is your preferred contact time?"

User: "morning"

[Agent calls submit_contact_info(contact_time="morning")]
  → save action: contact_time → global variable
  → no next field (terminal)
  → workflow completes

Agent: "Thank you! I have all your information."
```

### State Evolution

**After Step 1 (COLLECT\_NAME):**

```json theme={null}
{
  "current_step_id": "COLLECT_EMAIL",
  "completed_steps": ["COLLECT_NAME"],
  "workflow_completed": false,
  "global_variables": {
    "user_name": "Alice"
  }
}
```

**After Step 2 (COLLECT\_EMAIL):**

```json theme={null}
{
  "current_step_id": "COLLECT_TIME",
  "completed_steps": ["COLLECT_NAME", "COLLECT_EMAIL"],
  "workflow_completed": false,
  "global_variables": {
    "user_name": "Alice",
    "user_email": "alice@example.com"
  }
}
```

**After Step 3 (COLLECT\_TIME):**

```json theme={null}
{
  "current_step_id": "COLLECT_TIME",
  "completed_steps": ["COLLECT_NAME", "COLLECT_EMAIL", "COLLECT_TIME"],
  "workflow_completed": true,
  "global_variables": {
    "user_name": "Alice",
    "user_email": "alice@example.com",
    "contact_time": "morning"
  }
}
```

***

## Try It

To test this workflow in the Syllable Console:

1. Create a new tool with the JSON above
2. Assign it to an agent
3. Start a conversation and say "hello"
4. Provide your name when asked
5. Provide your email when asked
6. Provide your preferred time when asked
7. Observe the workflow completing after all three steps

***

## What's Next

This example creates a functional contact form, but the experience feels robotic—no greeting, no sense of progress. In [Example 4: Lifecycle Actions](./example-04-lifecycle), you'll learn how to:

* Add welcome messages with `on.enter` hooks
* Display progress indicators ("Step 1 of 3")
* Use the `say` action for verbatim text delivery
* Track progress with counters
