YFlow: A YAML-Based Conversational Flow Language

Overview

YFlow is a declarative, YAML‑based language designed to define conversational flows in a structured, readable, and secure way. It allows teams to describe how a conversation progresses—step by step—without writing imperative code.

YFlow focuses on conversation logic, not implementation details.

It focuses on:

  • Declarative over Imperative - Describe what the flow does, not how

  • YAML-Native Features - Leverage anchors, aliases, and native multi-line strings

  • Graph as Data - The flow graph is just structured data, easily transformed

  • Composability - Build complex flows from simple, reusable pieces

  • Human & Machine Readable - Clear enough for humans, parse able for tools

Major Changes: Customer v2 JSON → YFlow

This section summarizes the key serialization changes required when transitioning from Customer v2's JSON storage format to YFlow. These changes affect the parser (YFlow → Customer v2 model), emitter (Customer v2 model → YFlow), and the integration resolution layer.

1. Node Identity: UUID → Human-Readable Name

Current (Customer v2 JSON): Every node has a system-generated UUID as its identity (noded). Some nodes also have a separate display name (e.g., Agent nodes have a name field).

Target (YFlow): The node name (YAML map key) is the identifier. There are no UUIDs in the flow model — names are the primary and only identity. This is the same model as variables in a programming language: the name is the reference.

Naming Rules:

  • Names must be unique within the flow

  • Any case is allowed (snake_case, camelCase, PascalCase, etc.)

  • Spaces are allowed — use YAML quotes: "Main Agent": 1–64 characters

  • Names with spaces require quotes in next: references too: next: "Main Agent"

Auto-Generated Name Convention (Emitter — Customer v2 → YFlow):

The emitter uses snake_case when auto-generating names from existing Customer v2 data:

Node Type

Name Source

Example

Start

Always start

start

End

end or end_N if multiple

end, end_1

Agent

Existing name field (slugified to snake_case)

main_agent

Menu

Existing name field (slugified to snake_case)

main_menu

Say

say_N (sequential)

say_1, say_2

API

{integration}_{method} from integration/method names

banking_api_get_balance

Transfer

transfer_N or slugified label

transfer_to_sales

Tools

{agent_name}_tools or tools_N

main_agent_tools

Branch

branch_N or descriptive name

check_balance

Set

set_N or descriptive name

set_defaults

Handling Nodes That Already Have Names:

  • Agent and Menu nodes already store a name field in Customer v2. The emitter slugifies this to snake_case (e.g., "Main Agent" → main_agent).

  • If two nodes produce the same slug, append _N suffix (main_agent, main_agent_2).

  • API nodes derive their name from the integration and method identifiers, not from UUIDs.

  • Say, Transfer, End nodes that have no name in Customer v2 get auto-generated sequential names.

Name as Identity (Parser — YFlow → Customer v2):

  • The node name is the identity. No UUIDs are generated or stored.

  • All next: references use names directly — no resolution step needed.

  • Name uniqueness is enforced within the flow at parse time.

  • Reporting, analytics, and debugging all use the human-readable name.

2. API Parameters: Variables and Constants

YFlow APIs support variables and constants as parameters to drive conversational behavior in a controlled, secure, and predictable manner. These parameters define what data is available to a flow and how it may be used during evaluation.

Current ( Customer v2 JSON): The requestVariables field maps parameter names to variable names (bare strings). There is no support for constant values — every parameter must be a variable reference.

Target (YFlow): The params: field supports both {variable} references and literal constants (strings, numbers, booleans).

Customer v2 Enhancement Required: Extend requestVariables (or add a parallel field) to distinguish variable references from constants. The parser/emitter must handle both directions:

Direction

`{variable}` param

Constant param

Export (YFlow → Customer v2)

Strip braces, add to variable map

Store as constant (new Customer v2 capability)

Import ( Customer v2 → YFlow)

Wrap in {...}

Emit as bare value

3. Integration Resolution: Optional UUIDs in YFlow

YFlow supports optional UUID‑based integration resolution to flexibly connect conversational flows with external systems, actions, or integrations—without tightly coupling flows to specific implementations.

This mechanism allows flows to remain portable, secure, and environment‑agnostic.

Problem: The runtime resolves API and MCP integrations by UUID only. Integration display names can change. YFlow needs human-readable names for portability, but UUIDs for runtime performance.

Solution: YFlow includes both human-readable names and optional UUID fields (integration_id, method_id, connection_id). The *_id fields are:

  • Optional — absent in exported/shared YFlow files (portable between environments)

  • Resolved at save time — when a flow is saved, the resolver populates *_id fields from the integration service

  • Used at load time — runtime reads *_id fields directly (no lookup needed)

API Node:

An API Node in YFlow represents a declarative interaction point where a conversational flow invokes an external API through a resolved integration. It defines what data is sent, what response is expected, and how the conversation proceeds—without embedding execution logic or code.

Tools/MCP Node:

A Tools / MCP Node in YFlow represents a controlled invocation of an external tool or MCP (Model Context Protocol) capability within a conversational flow. It allows YFlow to safely leverage specialized tools (e.g., retrieval, computation, orchestration, or AI‑assisted functions) without exposing the flow to executable code or system‑level access.

Lifecycle:

Operation

`*_id` fields

Behavior

Save

Absent or stale

Resolver looks up names → populates UUIDs → stores YFlow with IDs

Save

Present and valid

Stored as-is (no lookup needed)

Load

Present

Runtime uses UUIDs directly — no integration service call

Load

Absent

Resolver looks up names → populates UUIDs (fallback)

Export

Stripped

Emit names only — portable YFlow for sharing

Import

Absent

Resolver looks up names → populates UUIDs → save

Error Handling

Error handling in YFlow is explicit, deterministic, and declarative. Rather than relying on exceptions, try/catch blocks, or hidden runtime behavior, YFlow requires all error paths to be clearly modeled in the flow definition.

This ensures failures are safe, observable, and intentional.

  • Import/Save: If an integration name cannot be resolved, fail with a clear error: "Unknown integration: banking_api".

  • Load: If a *_id UUID is present but no longer valid (deleted integration), fall back to name lookup. If name also fails, report error.

  • Export: Always strip *_id fields — exported YFlow is environment-independent.

Last updated

Was this helpful?