Workflow YAML Specification#
Version: 1.0
Status: Stable
The OpenIntent Workflow YAML format provides a declarative way to define multi-agent coordination workflows. This specification describes the complete schema, validation rules, and execution semantics.
Overview#
A workflow YAML file describes:
- Metadata - Name, version, description
- Agents - Declared agent capabilities
- Phases - Sequential or parallel work units assigned to agents
- Governance - Approval gates, budgets, timeouts
- LLM Configuration - Provider settings for AI-powered agents
Document Structure#
openintent: "1.0" # Required: Protocol version
info: # Required: Workflow metadata
name: "..." # Required: Workflow name
version: "1.0.0" # Optional: Semantic version
description: "..." # Optional: Description
governance: # Optional: Governance rules
require_approval: {...}
max_cost_usd: 10.00
timeout_hours: 24
agents: # Optional: Agent declarations
agent-id:
description: "..."
capabilities: [...]
llm: # Optional: LLM provider config
provider: "openai"
model: "gpt-5.2"
workflow: # Required: Workflow phases
phase_name:
title: "..."
assign: "agent-id"
...
types: # Optional: Type definitions
TypeName:
field: type
Required Fields#
openintent#
Protocol version identifier. Currently "1.0".
info#
Workflow metadata object.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Human-readable workflow name |
version |
string | No | Semantic version (default: "1.0.0") |
description |
string | No | Detailed description |
info:
name: "Research Pipeline"
version: "2.1.0"
description: "Research a topic and create a summary report"
workflow#
Map of phase definitions. Each key is the phase name (internal identifier).
workflow:
research:
title: "Gather Research"
description: "Research the topic thoroughly"
assign: researcher
summarize:
title: "Create Summary"
assign: summarizer
depends_on: [research]
Phase Configuration#
Each phase in the workflow section supports these fields:
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | No | Display title (defaults to phase name) |
description |
string | No | What this phase should accomplish |
assign |
string | Yes | Agent ID to assign this phase to |
depends_on |
list[string] | No | Phase names that must complete first |
constraints |
list[string] | No | Rules/parameters for the agent |
initial_state |
object | No | Initial state values |
inputs |
object | No | Input mappings wired by the executor before handoff (RFC-0024) |
outputs |
object | No | Declared output keys and types; executor validates on completion (RFC-0024) |
skip_when |
string | No | Condition expression to skip phase |
Input/Output Contracts (RFC-0024)#
The inputs and outputs fields establish typed I/O contracts between phases. The executor — not the agent — owns the wiring: it pre-populates ctx.input from resolved upstream outputs before calling the agent handler, and it validates the agent's return dict against declared outputs before marking the task complete.
inputs Field#
inputs is a mapping from local key names to upstream phase output references. The executor resolves each reference and injects the value into ctx.input before the agent handler is called.
Syntax for input mapping values:
| Pattern | Description |
|---|---|
{phase_name}.{key} |
Output key {key} from completed upstream phase {phase_name} |
$trigger.{key} |
Value from the workflow trigger payload |
$initial_state.{key} |
Value from the phase's initial_state |
Rules:
- Every {phase_name} referenced in an input mapping must appear in the phase's depends_on list.
- If the upstream phase declares outputs, the referenced key must appear there.
- If any declared input cannot be resolved, the executor rejects the task claim with UnresolvableInputError.
workflow:
run_analysis:
title: "Run Analysis"
assign: analytics-agent
depends_on: [fetch_financials, fetch_hr_data]
inputs:
fin_revenue: fetch_financials.revenue
fin_expenses: fetch_financials.expenses
hr_headcount: fetch_hr_data.headcount
quarter: $trigger.quarter
outputs:
findings: array
risk_level: string
violations_found: boolean
The agent for run_analysis reads:
revenue = ctx.input["fin_revenue"] # from fetch_financials output
headcount = ctx.input["hr_headcount"] # from fetch_hr_data output
quarter = ctx.input["quarter"] # from trigger
outputs Field#
outputs declares the keys and types that the agent must return. The executor validates the agent's output dict against this declaration at completion time.
Type values may be:
- Primitive strings: string, number, boolean, object, array
- Type names from the workflow's types block
- An object with type and optional required modifier
types:
Finding:
source: string
content: string
confidence: number
workflow:
research:
title: "Research Phase"
assign: researcher
outputs:
sources: array
findings: Finding
summary: string
warnings:
type: array
required: false # optional output — will not fail validation if absent
Validation behavior:
- Required keys (default) must be present in the agent's return dict, or the executor raises MissingOutputError.
- If a value's type does not match the declaration, the executor raises OutputTypeMismatchError.
- No type coercion is performed. Validation only.
- Extra keys returned beyond what is declared are accepted and recorded.
End-to-End Wiring Example#
openintent: "1.0"
info:
name: "Research Pipeline"
types:
Finding:
source: string
content: string
confidence: number
workflow:
research:
title: "Gather Research"
assign: researcher
inputs:
topic: $trigger.topic
outputs:
sources: array
findings: Finding
analysis:
title: "Analyze Findings"
assign: analyst
depends_on: [research]
inputs:
research_findings: research.findings
source_list: research.sources
outputs:
insights: string
recommendations: array
report:
title: "Write Report"
assign: writer
depends_on: [analysis]
inputs:
insights: analysis.insights
recommendations: analysis.recommendations
outputs:
report_url: string
report_summary: string
The executor guarantees:
1. research is called with ctx.input = {"topic": <trigger value>}
2. analysis is only claimable after research completes with both sources and findings present
3. analysis is called with ctx.input = {"research_findings": ..., "source_list": ...}
4. analysis completion is rejected if insights or recommendations are missing
5. report is called with ctx.input = {"insights": ..., "recommendations": ...}
No agent knows about the others. No agent reads raw intent state.
RFC-Specific Phase Fields#
| Field | Type | RFC | Description |
|---|---|---|---|
retry |
object | RFC-0010 | Retry policy configuration |
leasing |
object | RFC-0003 | Scope leasing configuration |
cost_tracking |
object | RFC-0009 | Cost budget and tracking |
attachments |
list[object] | RFC-0005 | File attachments |
permissions |
string | list | object | RFC-0011 | Access control, delegation, and context (see Permissions) |
Phase Example#
workflow:
analysis:
title: "Analyze Document"
description: "Extract key information from the document"
assign: analyzer-agent
depends_on:
- extraction
constraints:
- "Be thorough but concise"
- "Focus on actionable items"
initial_state:
phase: "analysis"
depth: "comprehensive"
retry:
max_attempts: 3
backoff: exponential
initial_delay_ms: 1000
outputs:
- findings
- recommendations
Agents Section#
Declare agents referenced in the workflow. While optional, it provides documentation, validation, and access control configuration.
agents:
researcher:
description: "Gathers and analyzes information from sources"
capabilities:
- web-search
- document-analysis
- summarization
summarizer:
description: "Creates concise summaries from research"
capabilities:
- text-generation
- formatting
default_permission: read
auditor:
description: "Reviews outputs for compliance"
capabilities:
- compliance
- review
default_permission: read
approval_required: true
| Field | Type | Description |
|---|---|---|
description |
string | What this agent does |
capabilities |
list[string] | Agent capabilities for validation and access decisions |
default_permission |
string | Default permission when joining intents: read, write, admin |
approval_required |
boolean | Whether this agent must request access approval |
Governance Section#
Configure approval gates, budgets, and timeouts.
governance:
require_approval:
when: "risk.category == 'HIGH'"
approvers:
- "legal-team"
- "compliance-officer"
timeout_hours: 24
on_timeout: "escalate"
max_cost_usd: 10.00
timeout_hours: 48
escalation:
contact: "admin@company.com"
after_hours: 4
| Field | Type | Description |
|---|---|---|
require_approval |
object | Conditional approval configuration |
max_cost_usd |
number | Maximum workflow cost budget |
timeout_hours |
number | Maximum workflow duration |
escalation |
object | Escalation contact info |
Approval Configuration#
| Field | Type | Description |
|---|---|---|
when |
string | Condition expression for requiring approval |
approvers |
list[string] | List of approver IDs |
timeout_hours |
number | Time to wait for approval |
on_timeout |
string | Action on timeout: "escalate", "abort", "proceed" |
LLM Configuration#
Configure the LLM provider for AI-powered agents.
llm:
provider: "openai" # openai, anthropic, or env
model: "gpt-5.2" # Model name (optional)
temperature: 0.7 # Sampling temperature
max_tokens: 4096 # Max response tokens
system_prompt: "..." # Global system prompt
| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | "openai" |
LLM provider |
model |
string | Provider default | Model identifier |
temperature |
number | 0.7 |
Sampling temperature |
max_tokens |
integer | 4096 |
Maximum tokens |
system_prompt |
string | "" |
System prompt |
Provider Defaults#
| Provider | Default Model | Environment Variable |
|---|---|---|
openai |
gpt-5.2 |
OPENAI_API_KEY |
anthropic |
claude-sonnet-4-20250514 |
ANTHROPIC_API_KEY |
env |
gpt-5.2 |
Auto-detect from env |
Retry Configuration#
Configure retry behavior for failed phases (RFC-0010).
retry:
max_attempts: 5
backoff: exponential # constant, linear, exponential
initial_delay_ms: 1000
max_delay_ms: 60000
retryable_errors:
- "TIMEOUT"
- "RATE_LIMIT"
fallback_agent: "backup-agent"
| Field | Type | Description |
|---|---|---|
max_attempts |
integer | Maximum retry attempts |
backoff |
string | Backoff strategy |
initial_delay_ms |
integer | Initial delay in milliseconds |
max_delay_ms |
integer | Maximum delay cap |
retryable_errors |
list[string] | Error types to retry |
fallback_agent |
string | Agent to use after max attempts |
Leasing Configuration#
Configure scope leasing for exclusive access (RFC-0003).
leasing:
strategy: per_section # global, per_section, custom
scope: "section:{{section.id}}"
ttl_seconds: 60
wait_for_lock: true
| Field | Type | Description |
|---|---|---|
strategy |
string | Leasing strategy |
scope |
string | Scope pattern (supports interpolation) |
ttl_seconds |
integer | Lease time-to-live |
wait_for_lock |
boolean | Wait for lock or fail immediately |
Cost Tracking Configuration#
Configure budget and cost tracking (RFC-0009).
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable cost tracking |
budget_usd |
number | Phase budget in USD |
alert_at_percent |
integer | Alert threshold percentage |
Attachments Configuration#
Declare expected attachments (RFC-0005).
attachments:
- filename: "report.pdf"
content_type: "application/pdf"
- filename: "data.json"
content_type: "application/json"
| Field | Type | Description |
|---|---|---|
filename |
string | Attachment filename |
content_type |
string | MIME type |
Permissions Configuration#
Unified access control for phases (RFC-0011). The permissions field replaces the previous separate access, delegation, and context fields with a single block that supports both shorthand and full forms.
Shorthand Forms#
For the common cases, permissions accepts a string or a list:
# Open access (default — any agent, no restrictions)
permissions: open
# Only the assigned agent can access
permissions: private
# Specific agents get write access
permissions: [analyzer-agent, auditor]
| Shorthand | Equivalent |
|---|---|
open |
Any agent can access at the default permission level |
private |
Only the assigned agent has access |
[agent-a, agent-b] |
Listed agents get write; others get read |
Full Form#
For fine-grained control, permissions accepts an object:
permissions:
policy: restricted # open, restricted, private
default: read # default permission for unlisted agents
allow:
- agent: "analyzer-agent"
level: write
- agent: "auditor"
level: read
expires: "2026-12-31T00:00:00Z"
delegate:
to: ["specialist-bot"] # who can receive delegated work
level: read # permission granted to delegates
context: [dependencies, peers] # what context to auto-inject
Permissions Fields#
| Field | Type | Default | Description |
|---|---|---|---|
policy |
string | open |
open (any agent), restricted (declared agents), private (assigned only) |
default |
string | read |
Default permission for unlisted agents: read, write, admin |
allow |
list[object] | [] |
Explicit access grants |
delegate |
object | null |
Delegation configuration |
context |
list[string] | auto |
Context fields to inject, or auto for permission-based defaults |
Allow Entry#
| Field | Type | Required | Description |
|---|---|---|---|
agent |
string | Yes | Agent or role ID |
level |
string | Yes | Permission level: read, write, admin |
expires |
string | No | ISO 8601 expiration timestamp |
Delegate#
| Field | Type | Default | Description |
|---|---|---|---|
to |
list[string] | [] |
Allowed delegation targets (empty = no delegation) |
level |
string | read |
Permission granted to delegates |
Context Injection#
The context field controls what information is auto-populated in intent.ctx:
| Value | Description |
|---|---|
auto |
Server decides based on the agent's permission level (default) |
[field, ...] |
Explicit list of fields to inject |
none |
No context injection |
Available context fields: dependencies, peers, parent, events, acl, delegated_by.
Governance Access Review#
Access request handling at the workflow level (in the governance section):
governance:
access_review:
on_request: defer # approve, deny, defer
approvers: ["admin-agent"]
timeout_hours: 4
Permissions Examples#
Simple — restrict to two agents:
workflow:
analysis:
title: "Analyze Document"
assign: analyzer-agent
permissions: [analyzer-agent, auditor]
Moderate — add delegation and context:
workflow:
analysis:
title: "Analyze Document"
assign: analyzer-agent
permissions:
policy: restricted
allow:
- agent: "analyzer-agent"
level: write
- agent: "auditor"
level: read
delegate:
to: ["specialist-bot"]
level: read
context: [dependencies, peers, acl]
In the agent implementation, access control is automatic:
@Agent("analyst-agent")
class Analyst:
@on_assignment
async def work(self, intent):
if intent.ctx.delegated_by:
print(f"Delegated by: {intent.ctx.delegated_by}")
if needs_help(intent):
await intent.delegate("specialist-bot")
return {"analysis": result}
@on_access_requested
async def policy(self, request):
return "approve" if request.level == "read" else "defer"
Types Section#
Define types for validation and documentation.
types:
Finding:
source: string
content: string
confidence: number
RiskLevel:
enum: ["low", "medium", "high", "critical"]
Dependency Resolution#
Dependencies are resolved using topological sort:
- Phases with no
depends_onrun immediately (can be parallel) - A phase starts only when ALL dependencies are
completed - Circular dependencies are detected and rejected at validation time
workflow:
# These two run in parallel (no dependencies)
research:
assign: researcher
competitor_analysis:
assign: analyst
# This waits for both above to complete
synthesis:
assign: synthesizer
depends_on: [research, competitor_analysis]
# This waits for synthesis
report:
assign: reporter
depends_on: [synthesis]
Validation Rules#
The parser validates:
- Required fields -
openintent,info.name,workflow, phaseassign - Dependency references - All
depends_onmust reference valid phase names - Circular dependencies - No cycles allowed in dependency graph
- Agent references - Warns if
assignreferences undeclared agent
Example Validation Errors#
WorkflowValidationError: Missing 'openintent' version field
Hint: Add 'openintent: "1.0"' at the top of your file
WorkflowValidationError: Phase 'synthesis' depends on unknown phase 'resarch'
Hint: Available phases: research, analysis, report
WorkflowValidationError: Circular dependency detected: a -> b -> c -> a
Hint: Remove one of the dependencies to break the cycle
Execution#
CLI Execution#
# Run a workflow
openintent run workflow.yaml
# With options
openintent run workflow.yaml --server http://localhost:8000
openintent run workflow.yaml --api-key my-key
openintent run workflow.yaml --timeout 600
openintent run workflow.yaml --output result.json
# Validate without running
openintent validate workflow.yaml
# List sample workflows
openintent list
# Create from template
openintent new "My Workflow"
Programmatic Execution#
from openintent.workflow import WorkflowSpec
# Load from file
spec = WorkflowSpec.from_yaml("workflow.yaml")
# Or from string
spec = WorkflowSpec.from_string(yaml_content)
# Convert to PortfolioSpec for manual execution
portfolio = spec.to_portfolio_spec()
# Or execute directly
result = await spec.run(
server_url="http://localhost:8000",
api_key="dev-user-key",
timeout=300,
verbose=True
)
Complete Example#
openintent: "1.0"
info:
name: "Research Pipeline"
version: "1.0.0"
description: "Research a topic and create a comprehensive report"
governance:
max_cost_usd: 5.00
timeout_hours: 2
access_review:
on_request: defer
approvers: ["lead-researcher"]
agents:
researcher:
description: "Gathers information from various sources"
capabilities: [web-search, summarization]
analyst:
description: "Analyzes and synthesizes information"
capabilities: [analysis, reasoning]
default_permission: read
writer:
description: "Creates polished written output"
capabilities: [writing, formatting]
llm:
provider: openai
model: gpt-5.2
temperature: 0.7
workflow:
research:
title: "Gather Research"
description: "Research the topic from multiple sources"
assign: researcher
constraints:
- "Use at least 3 sources"
- "Focus on recent information"
initial_state:
phase: "research"
retry:
max_attempts: 3
backoff: exponential
permissions:
delegate:
to: ["analyst"]
outputs:
- sources
- findings
analysis:
title: "Analyze Findings"
description: "Synthesize research into key insights"
assign: analyst
depends_on: [research]
permissions:
policy: restricted
allow:
- agent: "analyst"
level: write
- agent: "writer"
level: read
context: [dependencies, peers, acl]
cost_tracking:
enabled: true
budget_usd: 2.00
outputs:
- insights
- recommendations
report:
title: "Write Report"
description: "Create a comprehensive written report"
assign: writer
depends_on: [analysis]
permissions:
context: [dependencies, delegated_by]
attachments:
- filename: "report.md"
content_type: "text/markdown"
See Also#
- Workflows User Guide - Step-by-step usage guide
- Workflow API Reference - Python API documentation
- Sample Workflows - Example workflows