Skip to main content
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:
{
  "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:
"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:
"on": {
  "submit": [
    {"action": "save"}
  ]
}
Without parameters: Saves all step inputs to global variables with matching names. With specific inputs:
{"action": "save", "inputs": ["user_name", "user_email"]}
With a custom name:
{"action": "save", "name": "contact_info.name", "inputs": ["user_name"]}

Transitional vs Terminal Steps

Step TypeHas nextBehavior
TransitionalYesMoves to next step after submission
TerminalNoCompletes 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:
{
  "name": "submit_contact_info",
  "description": "Collect the user's name",
  "parameters": {"properties": {"user_name": {...}}}
}
At COLLECT_EMAIL:
{
  "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: "[email protected]"

[Agent calls submit_contact_info(user_email="[email protected]")]
  → 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):
{
  "current_step_id": "COLLECT_EMAIL",
  "completed_steps": ["COLLECT_NAME"],
  "workflow_completed": false,
  "global_variables": {
    "user_name": "Alice"
  }
}
After Step 2 (COLLECT_EMAIL):
{
  "current_step_id": "COLLECT_TIME",
  "completed_steps": ["COLLECT_NAME", "COLLECT_EMAIL"],
  "workflow_completed": false,
  "global_variables": {
    "user_name": "Alice",
    "user_email": "[email protected]"
  }
}
After Step 3 (COLLECT_TIME):
{
  "current_step_id": "COLLECT_TIME",
  "completed_steps": ["COLLECT_NAME", "COLLECT_EMAIL", "COLLECT_TIME"],
  "workflow_completed": true,
  "global_variables": {
    "user_name": "Alice",
    "user_email": "[email protected]",
    "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, 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