Anatomy of a Workflow
A workflow consists of:
Metadata : Name, description, categories, tags, visibility
Versions : Each version holds a complete definition
Definition : Steps, input/output schema, retry config, and timeouts
{
"name" : "Text to Image Generator" ,
"description" : "Generates images from text prompts" ,
"categories" : [ "image-generation" ],
"tags" : [ "image" , "ai" ],
"visibility" : "private" ,
"definition" : {
"version" : "v1" ,
"input_schema" : { ... },
"output_schema" : { ... },
"output_mapping" : { ... },
"timeout_seconds" : 900 ,
"retry" : {
"max_attempts" : 3 ,
"backoff_multiplier" : 2 ,
"initial_delay_seconds" : 1
},
"steps" : [ ... ]
}
}
Definition Fields
Field Type Required Description versionstring Yes Definition version identifier stepsarray Yes Array of steps to execute input_schemaobject No JSON Schema defining workflow inputs output_schemaobject No JSON Schema defining workflow outputs output_mappingobject No Map step outputs to workflow output timeout_secondsinteger No Maximum execution time for the entire workflow retryobject No Default retry configuration for all steps
Steps
Each step defines an operation in the workflow. Steps can be model invocations, HTTP requests, Python functions, parallel branches, conditional logic, or simple pass-through operations.
{
"id" : "generate_image" ,
"type" : "model" ,
"model" : "flux-1-1-pro" ,
"params" : {
"prompt" : "{{inputs.prompt}}" ,
"aspect_ratio" : "16:9"
}
}
Base Step Fields
All step types share these fields:
Field Type Required Description idstring Yes Unique identifier within the workflow typestring Yes Step type: model, http, python, parallel, choice, or pass continue_on_errorboolean No Continue workflow execution even if this step fails timeout_secondsinteger No Maximum execution time for this step retryobject No Step-level retry configuration
Retry Configuration
{
"retry" : {
"max_attempts" : 3 ,
"backoff_multiplier" : 2 ,
"initial_delay_seconds" : 1 ,
"retry_on" : [ "timeout" , "server_error" ]
}
}
Field Type Default Description max_attemptsinteger -Maximum number of retry attempts backoff_multipliernumber -Multiplier for exponential backoff between retries initial_delay_secondsnumber -Initial delay before first retry retry_onstring[] -Error types that trigger retries
Step Types
Type Description modelInvokes an AI model via each::api httpMakes an HTTP request to an external URL pythonExecutes a Python function parallelExecutes multiple branches concurrently choiceEvaluates a condition and routes to the matching branch passPasses input through without processing, optionally injecting a result
Model Steps
Invoke an AI model from the each::api catalog.
{
"id" : "generate_image" ,
"type" : "model" ,
"model" : "flux-2-max" ,
"version" : "1.0.0" ,
"params" : {
"prompt" : "{{inputs.prompt}}" ,
"aspect_ratio" : "16:9"
},
"fallback" : {
"enabled" : true ,
"model" : "flux-2-pro" ,
"params" : {
"prompt" : "{{inputs.prompt}}"
}
}
}
Field Type Required Description modelstring Yes AI model slug versionstring No Model version paramsobject Yes Model-specific parameters (supports template variables ) fallbackobject No Fallback configuration
HTTP Steps
Make HTTP requests to external APIs or services.
{
"id" : "call_api" ,
"type" : "http" ,
"url" : "https://api.example.com/process" ,
"method" : "POST" ,
"headers" : {
"Content-Type" : "application/json"
},
"body" : {
"data" : "{{step1.primary}}"
},
"auth" : {
"type" : "bearer" ,
"token" : "{{inputs.api_token}}"
},
"timeout" : 30000
}
Field Type Required Default Description urlstring Yes -Request URL (supports template variables) methodstring No GETHTTP method: GET, POST, PUT, DELETE, PATCH headersobject No -Request headers query_paramsobject No -URL query parameters bodyany No -Request body authobject No -Authentication configuration timeoutinteger No -Request timeout in milliseconds follow_redirectsboolean No -Follow HTTP redirects validate_sslboolean No -Validate SSL certificates
Auth Configuration
Field Type Description typestring Auth type: basic, bearer, or api_key usernamestring For basic auth passwordstring For basic auth tokenstring For bearer auth
Python Steps
Execute Python code within the workflow.
{
"id" : "transform_data" ,
"type" : "python" ,
"code" : "result = inputs['text'].upper()" ,
"inputs" : {
"text" : "{{step1.primary}}"
}
}
Field Type Required Description codestring Yes Python code to execute inputsobject No Input variables available in the code (supports template variables)
Parallel Steps (Branching)
Use type: "parallel" to run multiple branches concurrently. Each branch contains its own sequence of steps that execute independently.
{
"id" : "step1" ,
"type" : "parallel" ,
"branches" : [
{
"name" : "Branch 1" ,
"steps" : [
{
"id" : "step1_branch_0" ,
"type" : "model" ,
"model" : "kling-v3-pro-image-to-video" ,
"params" : {
"prompt" : "A bee collecting nectar, wings vibrating" ,
"start_image_url" : "https://example.com/bee.png" ,
"duration" : "5" ,
"aspect_ratio" : "16:9"
}
}
]
},
{
"name" : "Branch 2" ,
"steps" : [
{
"id" : "step1_branch_1" ,
"type" : "model" ,
"model" : "nano-banana-2-edit" ,
"params" : {
"prompt" : "Transform into Cubism style" ,
"image_urls" : [ "https://example.com/scene.png" ],
"aspect_ratio" : "16:9"
}
}
]
},
{
"name" : "Branch 3" ,
"steps" : [
{
"id" : "step1_branch_2" ,
"type" : "model" ,
"model" : "nano-banana-2-text-to-image" ,
"params" : {
"prompt" : "Ultra realistic photo of a modern summer store" ,
"aspect_ratio" : "16:9"
}
}
]
}
]
}
Branch Fields
Field Type Required Description namestring No Human-readable branch name stepsarray Yes Sequence of steps to execute within this branch
All branches kick off at the same time and run independently. The parallel step wraps up once every branch finishes.
Choice Steps (Conditional Logic)
Use type: "choice" to route execution based on a condition. The workflow evaluates the condition and picks either the condition_met_branch or the default_branch.
{
"id" : "step2" ,
"type" : "choice" ,
"condition" : {
"expression" : "$.step1.primary" ,
"operator" : "string_matches" ,
"value" : ".mp4"
},
"condition_met_branch" : {
"name" : "condition_met" ,
"step_id" : "step2_condition_met" ,
"steps" : [
{
"id" : "step2_branch_0" ,
"type" : "model" ,
"model" : "topaz-upscale-video" ,
"params" : {
"video" : "{{step1.primary}}" ,
"scale" : 2
}
}
]
},
"default_branch" : {
"name" : "default" ,
"step_id" : "step2_default" ,
"steps" : [
{
"id" : "step2_default" ,
"type" : "model" ,
"model" : "topaz-upscale-image" ,
"params" : {
"image_url" : "{{step1.primary}}" ,
"scale" : 2
}
}
]
}
}
Condition Types
Conditions can be simple (single comparison) or logical (combining multiple conditions).
Simple Condition
Compares a single expression against a value:
{
"expression" : "$.step1.primary" ,
"operator" : "string_matches" ,
"value" : ".mp4"
}
Field Type Required Description expressionstring Yes Value to evaluate using $ reference syntax (e.g., $.step1.primary, $.inputs.email) operatorstring Yes Comparison operator (see table below) valueany Yes Value to compare against
Logical Conditions
Combine multiple simple conditions using and, or, or not:
And (all must be true)
Or (any must be true)
Not (negate)
{
"and" : [
{ "expression" : "$.step1.primary" , "operator" : "string_matches" , "value" : ".png" },
{ "expression" : "$.inputs.upscale" , "operator" : "equals" , "value" : true }
]
}
Condition Operators
Comparison Operators
Operator Description Example equalsEquals comparison (auto-detects type: boolean, numeric, or string) $.inputs.count equals 5not_equalsNot equals comparison (auto-detects type) $.inputs.format not equals "raw"greater_thanNumeric greater than $.step1.output.score greater than 0.8less_thanNumeric less than $.step1.output.score less than 0.5greater_than_or_equalNumeric greater than or equal $.inputs.count >= 1less_than_or_equalNumeric less than or equal $.inputs.count <= 10
String Operators
Operator Description Example string_equalsExplicit string equality $.inputs.format string equals "png"string_matchesString pattern matching (contains) $.step1.primary matches ".mp4"
Array Operators
Operator Description Example inValue is in array $.inputs.format in ["png", "jpg", "webp"]not_inValue is not in array $.inputs.format not in ["gif", "bmp"]
Existence Operators
Operator Description Example existsField exists and is present $.step1.output.url existsnot_existsField does not exist $.step1.output.error not existsis_nullField is null $.step1.primary is nullis_not_nullField is not null $.step1.primary is not null
Existence operators (exists, not_exists, is_null, is_not_null) do not require a value field in the condition.
Choice Branch Fields
Field Type Required Description namestring Yes Branch identifier (condition_met or default) step_idstring Yes Unique ID for the branch stepsarray Yes Steps to execute in this branch
Pass Steps
A pass-through step that does no processing. Super handy for injecting static values or as a placeholder.
{
"id" : "static_config" ,
"type" : "pass" ,
"result" : {
"style" : "cinematic" ,
"quality" : "high"
}
}
Field Type Required Description resultany No Static value to output result_pathstring No Path to place the result in the output
Define what inputs your workflow accepts using JSON Schema:
{
"input_schema" : {
"type" : "object" ,
"required" : [ "prompt" ],
"properties" : {
"prompt" : {
"type" : "string" ,
"description" : "Text prompt for generation"
},
"style" : {
"type" : "string" ,
"enum" : [ "realistic" , "artistic" , "anime" ],
"default" : "realistic"
},
"num_images" : {
"type" : "integer" ,
"minimum" : 1 ,
"maximum" : 4 ,
"default" : 1
}
}
}
}
Execution Flow
Steps run sequentially by default. Each step’s output is available to subsequent steps via parameter references . Parallel and choice steps let you add branching and conditional logic into the mix.
Sequential: inputs → step1 → step2 → step3 → output
Parallel: inputs → ┬─ Branch 1 ─┐
├─ Branch 2 ─┤→ next step
└─ Branch 3 ─┘
Conditional: inputs → step1 → choice ─┬─ condition met → step A
└─ default → step B
Step Outputs
Each completed step produces a StepOutput:
{
"step_id" : "generate_image" ,
"kind" : "model" ,
"status" : "completed" ,
"started_at" : "2025-01-15T10:00:00Z" ,
"completed_at" : "2025-01-15T10:00:12Z" ,
"output" : [ "https://storage.example.com/image1.png" ],
"primary" : "https://storage.example.com/image1.png" ,
"metadata" : {
"model" : "flux-1-1-pro" ,
"version" : "0.0.1" ,
"prediction_id" : "pred-123" ,
"elapsed_seconds" : 12.5 ,
"params" : { ... }
}
}
Field Description kindStep type: model, http, python, parallel, choice, pass statusExecution status: running, completed, failed, cancelled, queued, skipped outputFull step output (string, array, or object) primaryFirst/main result for quick access metadataStep configuration and runtime info errorError message if the step failed error_causeDetailed error cause
Choice Step Output
For choice steps, the output includes which branch was selected:
{
"step_id" : "step2" ,
"kind" : "choice" ,
"status" : "completed" ,
"selected" : "condition_met" ,
"metadata" : {
"choice_branch_selected" : "condition_met" ,
"branch_name" : "condition_met" ,
"choice_step_id" : "step2_condition_met"
}
}
Fallback Output
When a fallback kicks in, a separate step output appears with a _fallback suffix:
{
"step_id" : "step1_fallback" ,
"status" : "completed" ,
"fallback" : {
"fallback_from_step" : "step1" ,
"reason" : "primary_failed"
},
"metadata" : {
"fallback_used" : true ,
"primary_error" : "Model timeout"
}
}
Fallback reasons: primary_failed (the primary step failed and fallback kicked in) or not_triggered (primary succeeded, so fallback was skipped).