Skip to main content

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

FieldTypeRequiredDescription
versionstringYesDefinition version identifier
stepsarrayYesArray of steps to execute
input_schemaobjectNoJSON Schema defining workflow inputs
output_schemaobjectNoJSON Schema defining workflow outputs
output_mappingobjectNoMap step outputs to workflow output
timeout_secondsintegerNoMaximum execution time for the entire workflow
retryobjectNoDefault 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:
FieldTypeRequiredDescription
idstringYesUnique identifier within the workflow
typestringYesStep type: model, http, python, parallel, choice, or pass
continue_on_errorbooleanNoContinue workflow execution even if this step fails
timeout_secondsintegerNoMaximum execution time for this step
retryobjectNoStep-level retry configuration

Retry Configuration

{
  "retry": {
    "max_attempts": 3,
    "backoff_multiplier": 2,
    "initial_delay_seconds": 1,
    "retry_on": ["timeout", "server_error"]
  }
}
FieldTypeDefaultDescription
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

TypeDescription
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}}"
    }
  }
}
FieldTypeRequiredDescription
modelstringYesAI model slug
versionstringNoModel version
paramsobjectYesModel-specific parameters (supports template variables)
fallbackobjectNoFallback 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
}
FieldTypeRequiredDefaultDescription
urlstringYes-Request URL (supports template variables)
methodstringNoGETHTTP method: GET, POST, PUT, DELETE, PATCH
headersobjectNo-Request headers
query_paramsobjectNo-URL query parameters
bodyanyNo-Request body
authobjectNo-Authentication configuration
timeoutintegerNo-Request timeout in milliseconds
follow_redirectsbooleanNo-Follow HTTP redirects
validate_sslbooleanNo-Validate SSL certificates

Auth Configuration

FieldTypeDescription
typestringAuth type: basic, bearer, or api_key
usernamestringFor basic auth
passwordstringFor basic auth
tokenstringFor bearer auth

Python Steps

Execute Python code within the workflow.
{
  "id": "transform_data",
  "type": "python",
  "code": "result = inputs['text'].upper()",
  "inputs": {
    "text": "{{step1.primary}}"
  }
}
FieldTypeRequiredDescription
codestringYesPython code to execute
inputsobjectNoInput 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

FieldTypeRequiredDescription
namestringNoHuman-readable branch name
stepsarrayYesSequence 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"
}
FieldTypeRequiredDescription
expressionstringYesValue to evaluate using $ reference syntax (e.g., $.step1.primary, $.inputs.email)
operatorstringYesComparison operator (see table below)
valueanyYesValue to compare against

Logical Conditions

Combine multiple simple conditions using and, or, or not:
{
  "and": [
    {"expression": "$.step1.primary", "operator": "string_matches", "value": ".png"},
    {"expression": "$.inputs.upscale", "operator": "equals", "value": true}
  ]
}

Condition Operators

Comparison Operators

OperatorDescriptionExample
equalsEquals comparison (auto-detects type: boolean, numeric, or string)$.inputs.count equals 5
not_equalsNot equals comparison (auto-detects type)$.inputs.format not equals "raw"
greater_thanNumeric greater than$.step1.output.score greater than 0.8
less_thanNumeric less than$.step1.output.score less than 0.5
greater_than_or_equalNumeric greater than or equal$.inputs.count >= 1
less_than_or_equalNumeric less than or equal$.inputs.count <= 10

String Operators

OperatorDescriptionExample
string_equalsExplicit string equality$.inputs.format string equals "png"
string_matchesString pattern matching (contains)$.step1.primary matches ".mp4"

Array Operators

OperatorDescriptionExample
inValue is in array$.inputs.format in ["png", "jpg", "webp"]
not_inValue is not in array$.inputs.format not in ["gif", "bmp"]

Existence Operators

OperatorDescriptionExample
existsField exists and is present$.step1.output.url exists
not_existsField does not exist$.step1.output.error not exists
is_nullField is null$.step1.primary is null
is_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

FieldTypeRequiredDescription
namestringYesBranch identifier (condition_met or default)
step_idstringYesUnique ID for the branch
stepsarrayYesSteps 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"
  }
}
FieldTypeRequiredDescription
resultanyNoStatic value to output
result_pathstringNoPath to place the result in the output

Input Schema

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": { ... }
  }
}
FieldDescription
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).
Last modified on March 6, 2026