Workflow YAML Format
Complete schema reference for Binex workflow files.
Root — WorkflowSpec
| Field | Type | Required | Description |
|---|---|---|---|
version |
int |
no | Schema version (default: 1, must be >= 1). See Versioning below. |
name |
str |
yes | Workflow name |
description |
str |
no | Workflow description (default: "") |
nodes |
dict[str, NodeSpec] |
yes | Map of node_id to node definition |
defaults |
DefaultsSpec |
no | Default settings applied to all nodes |
budget |
BudgetConfig |
no | Budget constraints for the run (see below) |
webhook |
WebhookConfig |
no | Webhook notification target (see below) |
Node — NodeSpec
| Field | Type | Required | Description |
|---|---|---|---|
id |
str |
no | Auto-set from the dict key |
agent |
str |
yes | Agent URI — local://, llm://, a2a://, human://, langchain://, crewai://, autogen://, or custom plugin prefix |
system_prompt |
str |
no | System prompt sent to the agent (supports file:// prefix) |
inputs |
dict[str, Any] |
no | Input key-value pairs; supports variable interpolation |
outputs |
list[str] |
yes | Artifact names this node produces |
depends_on |
list[str] |
no | Node IDs that must complete before this node runs |
config |
dict[str, Any] |
no | Per-node config forwarded to the adapter (see below) |
retry_policy |
RetryPolicy |
no | Override the default retry settings |
deadline_ms |
int |
no | Override the default deadline for this node |
when |
str |
no | Conditional execution expression (see below) |
cost |
NodeCostHint |
no | Optional cost estimate for planning (see below) |
budget |
float or NodeBudget |
no | Per-node budget limit (shorthand: budget: 0.50, full: budget: { max_cost: 0.50 }) |
output_schema |
dict |
no | JSON Schema for validating node output. Failed validation triggers auto-retry |
routing |
dict |
no | Per-node Gateway routing overrides (see below) |
config keys (LLM adapter)
| Key | Example | Effect |
|---|---|---|
api_base |
"http://localhost:11434" |
LiteLLM API base URL |
api_key |
"sk-..." |
Provider API key |
temperature |
0.7 |
Sampling temperature |
max_tokens |
4096 |
Max tokens in completion |
All config values are forwarded to litellm.acompletion() when not None.
External System Prompt — file://
The system_prompt field supports loading content from an external file using the file:// prefix.
Relative paths are resolved relative to the workflow YAML file's directory. Absolute paths are used as-is.
nodes:
researcher:
agent: "llm://openai/gpt-4"
system_prompt: "file://prompts/researcher.md"
outputs: [findings]
If the referenced file does not exist, workflow loading fails with a clear error message.
Conditional Execution — when
The when field enables conditional node execution based on upstream artifact values.
A node with a when condition is skipped (not failed) if the condition evaluates to false.
Skipped nodes count as resolved for downstream dependency purposes.
Operators:
| Operator | Example | Meaning |
|---|---|---|
== |
${review.decision} == approved |
Run only if artifact content equals "approved" |
!= |
${review.decision} != rejected |
Run only if artifact content does not equal "rejected" |
Example — approval gate with branching:
publish:
agent: "local://echo"
inputs:
final: "${revise.content}"
outputs: [result]
depends_on: [human_review]
when: "${human_review.decision} == approved"
discard:
agent: "local://echo"
inputs: {}
outputs: [notice]
depends_on: [human_review]
when: "${human_review.decision} == rejected"
The when field is commonly used with human://approve nodes but works with any artifact value.
Defaults — DefaultsSpec
| Field | Type | Default | Description |
|---|---|---|---|
deadline_ms |
int |
120000 |
Default deadline in milliseconds |
retry_policy |
RetryPolicy |
see below | Default retry policy for all nodes |
Retry — RetryPolicy
| Field | Type | Default | Description |
|---|---|---|---|
max_retries |
int |
1 |
Maximum retry attempts |
backoff |
"fixed" or "exponential" |
"exponential" |
Backoff strategy between retries |
Budget — BudgetConfig
Budget constraints limit the total cost of a workflow run. The orchestrator checks accumulated cost after each batch of nodes.
| Field | Type | Default | Description |
|---|---|---|---|
max_cost |
float |
— | Maximum allowed cost in the specified currency (must be > 0) |
currency |
str |
"USD" |
Currency code |
policy |
"stop" or "warn" |
"warn" |
What to do when budget is exceeded |
Budget Policies
| Policy | Behavior |
|---|---|
stop |
Skip all remaining nodes, set run status to "over_budget" |
warn |
Log a warning to stderr, continue execution |
Example:
name: budgeted-pipeline
budget:
max_cost: 5.0
policy: stop
nodes:
planner:
agent: "llm://gpt-4o"
outputs: [plan]
researcher:
agent: "llm://claude-sonnet-4-20250514"
outputs: [findings]
depends_on: [planner]
summarizer:
agent: "llm://gpt-4o"
outputs: [summary]
depends_on: [researcher]
If the accumulated cost exceeds $5.00 after the researcher node, the summarizer is skipped and the run status is "over_budget".
See the Budget & Cost Tracking Guide for more examples and patterns.
With --json, the run output includes budget information:
{
"status": "over_budget",
"total_cost": 5.23,
"budget": 5.0,
"remaining_budget": -0.23
}
Node Cost Hint — NodeCostHint
Optional cost estimate for planning purposes. Does not affect execution — purely informational.
| Field | Type | Default | Description |
|---|---|---|---|
estimate |
float |
0.0 |
Estimated cost for this node (must be >= 0) |
nodes:
expensive_node:
agent: "llm://gpt-4o"
outputs: [result]
cost:
estimate: 2.50
Per-Node Budget — NodeBudget
Individual nodes can have their own budget limits. The policy is inherited from the workflow-level budget.policy (default: stop).
| Field | Type | Default | Description |
|---|---|---|---|
max_cost |
float |
— | Maximum allowed cost for this node (must be > 0) |
Shorthand: budget: 0.50 is equivalent to budget: { max_cost: 0.50 }.
When both workflow and node budgets are defined, the effective limit is min(node_budget, remaining_workflow_budget).
Pre-check before retry: If a node has a budget and fails, the orchestrator checks remaining budget before each retry attempt. With policy: stop, the retry is skipped if budget is exhausted. With policy: warn, the user is prompted via click.confirm().
Post-check after execution: After each execution, if the node's accumulated cost exceeds its budget, the policy determines behavior: stop discards the result and marks the node failed; warn keeps the result and logs a warning.
Example:
name: per-node-budget
budget:
max_cost: 10.00
policy: stop
nodes:
planner:
agent: "llm://gpt-4o-mini"
outputs: [plan]
budget: 0.50 # shorthand
researcher:
agent: "llm://gpt-4o"
outputs: [findings]
depends_on: [planner]
budget:
max_cost: 3.00 # full form
summarizer:
agent: "llm://gpt-4o"
outputs: [summary]
depends_on: [researcher]
budget: 2.00
If the planner costs $0.60 (exceeding its $0.50 limit), it is marked as failed and dependent nodes do not run.
Output Schema Validation
Nodes can define a JSON Schema to validate their output. If the output fails validation and the node has retries remaining, Binex automatically retries the node.
| Field | Type | Default | Description |
|---|---|---|---|
output_schema |
dict |
None |
Standard JSON Schema object |
Example:
nodes:
extractor:
agent: "llm://openai/gpt-4o"
system_prompt: "Extract structured data. Return valid JSON."
outputs: [result]
output_schema:
type: object
properties:
title:
type: string
score:
type: number
minimum: 0
maximum: 100
required:
- title
- score
retry_policy:
max_retries: 2
If the LLM returns output that doesn't match the schema (e.g., missing title field or score out of range), the node is retried automatically. After all retries are exhausted, the node fails with a validation error.
The validator handles both JSON string output (parsed first) and dict output (validated directly).
Routing Overrides
When a Gateway is configured (either embedded via gateway.yaml or standalone via --gateway), individual nodes can override the default routing behavior using the routing field:
nodes:
critical_search:
agent: "a2a://research"
routing:
prefer: lowest_latency
timeout_ms: 10000
retry_count: 5
failover: true
| Key | Type | Default | Description |
|---|---|---|---|
prefer |
str |
"highest_priority" |
Selection strategy (highest_priority, lowest_latency, round_robin) |
timeout_ms |
int |
null |
Override request timeout for this node |
retry_count |
int |
null |
Override retry count for this node |
failover |
bool |
null |
Override failover setting for this node |
Routing overrides only apply when a Gateway is configured. Without a Gateway, the routing field is ignored.
Variable Interpolation
Two variable scopes are available inside inputs values:
| Syntax | Resolved | Description |
|---|---|---|
${user.*} |
Load time | Substituted from --var CLI arguments |
${node.*} |
Runtime | References an artifact produced by another node |
Example:
inputs:
query: "${user.query}" # --var query="LLM agents"
plan: "${planner.execution_plan}" # artifact from the planner node
Minimal Valid Workflow
name: minimal
nodes:
only_node:
agent: "local://echo"
system_prompt: ping
inputs:
msg: "hello"
outputs: [response]
No defaults, description, depends_on, or config required.
Versioning
Workflow files support schema versioning via the version field. This enables future schema changes with automatic migration.
version: 1
name: my-workflow
nodes:
step1:
agent: "local://echo"
outputs: [result]
Behavior
- Missing
versionfield: Defaults to version 1 (backward compatible with all existing workflows). A warning is logged. version: 1: Current version — no migration needed.version > CURRENT_VERSION: RaisesUnsupportedVersionErrorat load time. Upgrade Binex to use newer workflows.
Migration framework
When Binex upgrades its schema version, a migration chain transforms older workflow dicts step by step (v1 → v2 → ... → current). Migrations run in-memory at load time — the original YAML file is never modified.
Workflow snapshots
Every binex run stores a normalized, SHA256-deduplicated snapshot of the workflow definition in SQLite. This lets you:
- Inspect the exact workflow used in any past run via
binex debug <run-id> - Compare workflows between runs via
binex workflow diff <run1> <run2> - Reproduce runs even if the original YAML file has changed
Check the workflow version of any file:
binex workflow version examples/simple.yaml
## Webhook — `WebhookConfig`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `url` | `str` | yes | Webhook endpoint URL |
Webhook notifications are sent on run completion, failure, or budget exceeded. Can also be set via `BINEX_WEBHOOK_URL` environment variable.
```yaml
name: notified-pipeline
webhook:
url: "https://hooks.example.com/binex"
nodes:
step1:
agent: "local://echo"
outputs: [result]