EmbodiFlowDocs
Get startedDataWorkflows and algorithmsTraining and deploymentDeveloper reference
  • Workflows and algorithms

Workflows

  • How workflows work
  • Build your own workflow
  • Runs and logs

Algorithms

  • Algorithm catalog
  • Write a custom algorithm

Algorithm developer guide

  • Your first algorithm
    • Standardize
    • Check
    • Transform
    • Enrich
    • Segment
    • Curate
    • Export
  • Image algorithms
  • Use your algorithm in a workflow
  • Interface reference
  • SDK reference
  • Command line
  • MCP tools
  • Troubleshooting

© 2026 EmbodiFlow

llms.txtOpenAPI JSON
  1. Workflows and algorithms
  2. Algorithm developer guide
  3. Use your algorithm in a workflow

Use your algorithm in a workflow

Wire your algorithms and the built-in ones into a workflow, and understand exactly how each step runs, skips or stops.

An algorithm on its own does nothing until a workflow runs it. A workflow is a graph: an Input node, your steps and an Output node, connected port to port. Connections are the only edges, so the run order follows from what feeds what, and you never manage it by hand.

This page covers building a workflow in the editor, the file behind it, and the rules that decide how each step runs. Behind the canvas, every workflow is a YAML file in the embodiflow-workflow/v1 format, and you can edit either view.

Beta. Custom algorithms and workflows are in the product and ready to try, but they haven't been through a full acceptance on real data yet. Expect some rough edges, and check results before you rely on them.
Editing a project workflow, binding it and publishing it need the project permission to manage workflows (workflow:manage). Starting a run needs workflow:run.

Build it in the editor

Open Workflows, then the project workflow, or start with New curate workflow or New export workflow. The Input node sits on the left and the Output node on the right. The palette lists built-in and team algorithms together, grouped by the seven categories, and you can search it.

There are several ways to add and connect a step, so use whichever suits the moment:

Way to connectHow
Drop a cardDrop an algorithm on the canvas. It connects its main input to the nearest compatible output upstream. The editor tells you what it connected, and you can undo it.
Drag a wireDrag from a port into empty space. A search lists the algorithms that fit that port.
Click the plus on a portChoose Add a step after this output or Add a step before this input.
Use the side panelSelect a step, open Connections, choose Add a connection, then Choose an output. Outputs that don't fit aren't offered.
Drop on a connectionDrop an algorithm on a connection, or choose Insert a step here, to put it between the two ends.

Ports are coloured by data type. While you drag, only fitting ports light up, and a refusal says why, for example that it would make a loop. Hover a connection to see what it carries. The inspector edits parameters, the when conditions and onFail. The YAML view edits the same file, Visual switches back, and Tidy now arranges the canvas.

Problems, saving and publishing

The problems panel lists every problem with its code. A draft that still has problems is saved anyway, so you never lose work, but it can't be published, bound to a project or run until they're fixed; the editor says so next to Publish. A file that can't be read at all is refused.

Test on a sample

Test on sample runs the draft on up to three recordings of the project. Window in seconds is optional and runs only the start of each recording, up to 600 seconds. A test run never saves a result: the steps that save the recording and evaluate its quality are skipped, shown grey as "Skipped: a test run on a window saves no result.", and never green. You can still read every step's output in the run.

The standardizer and raw checks always read the whole upload, even with a window, because raw files have no time axis.

A complete workflow

Here is the built-in human-ego-standard workflow exactly as stored. It's a good model to read before you write your own.

apiVersion: embodiflow-workflow/v1
name: Human egocentric standard
description: Pre-check, standardize, calibrate and anonymize; then head pose and depth from the calibration, MANO hands made metric by stereo hand keypoints, the SMPL-H body fitted to the hands, a ROS 2 3D view, visual quality, episode proposals and a success label per episode.
type: processing
params:
  task: { type: string, default: "", title: Task text }
steps:
  - id: pre_check
    uses: embodiflow/raw-metrics@1
    inputs: { raw: input.raw }
    onFail: stop
  - id: standardize
    uses: auto
    inputs: { raw: input.raw }
    with: { task: "${{ params.task }}" }
  - id: calibrate
    uses: embodiflow/sensor-calibrate@1
    inputs: { recording: standardize.recording }
  - id: anonymize
    uses: embodiflow/anonymize@1
    inputs: { recording: standardize.recording }
  - id: hand_pose
    uses: embodiflow/hand-pose@1
    inputs: { recording: anonymize.recording, calibration: calibrate.features }
    with: { frame_stride: 6 }
  - id: head_pose
    uses: embodiflow/camera-pose-vio@1
    inputs: { recording: anonymize.recording, calibration: calibrate.features }
  - id: depth
    uses: embodiflow/stereo-depth@1
    inputs: { recording: anonymize.recording, calibration: calibrate.features }
  - id: hands
    uses: embodiflow/hand-mano@1
    inputs: { recording: anonymize.recording, head_pose: head_pose.features, stereo_hands: hand_pose.features, depth: depth.features }
  - id: body
    uses: embodiflow/body-pose@1
    inputs: { recording: anonymize.recording, head_pose: head_pose.features, hands: hands.features, depth: depth.features }
  - id: view3d
    uses: embodiflow/ego-vis@1
    inputs: { recording: anonymize.recording, head_pose: head_pose.features, depth: depth.features, hands: hands.features, body: body.features }
  - id: visual
    uses: embodiflow/visual-metrics@1
    inputs: { recording: anonymize.recording }
  - id: propose
    uses: embodiflow/propose-episodes@1
    inputs: { recording: anonymize.recording }
  - id: success
    uses: embodiflow/episode-success@1
    inputs: { recording: anonymize.recording, episodes: propose.episodes }
output:
  recording: anonymize.recording
  features: [calibrate.features, head_pose.features, depth.features, hands.features, body.features, view3d.features, success.features]
  checks: [pre_check.checks, anonymize.checks, calibrate.checks, hand_pose.checks, head_pose.checks, depth.checks, hands.checks, body.checks, view3d.checks, visual.checks]
  episodes: propose.episodes
FieldRule
typeprocessing, curate or export. It fixes what the Input node gives and what the Output node takes.
paramsRun parameters: { name: { type: string, number or boolean, default, title } }. A step uses one as a whole with value.
steps[].idLowercase letters, digits and _, up to 32 long, unique, and not input, output or params.
steps[].usesembodiflow/<name>@<major>, team/<name>@<major>, or auto for a standardize step.
steps[].inputsEvery required input connected: <port>: <stepId>.<port> or input.<port>.
steps[].withParameter values, checked against the algorithm's parameters.
steps[].whenOptional conditions.
steps[].onFailCheck steps only: continue (default) or stop.
steps[].enabledDefault true. A disabled step is kept and ignored.
outputWhat the Output node receives.

There is no needs, if, fallback list or locked step. Node positions live in a separate layout document, so moving boxes around never changes the workflow.

Built-in algorithms you can use

A step refers to a built-in algorithm by its reference. The name is its title in the console.

ReferenceNameCategoryLevel
embodiflow/anonymize@1Anonymizetransformrecording
embodiflow/body-pose@1Body pose (SMPL-H)enrichrecording
embodiflow/camera-pose-vio@1Head pose (visual-inertial)enrichrecording
embodiflow/canonicalize@1Canonicalize MCAPstandardizerecording
embodiflow/canonicalize-video@1Canonicalize videostandardizerecording
embodiflow/ego-vis@1Ego 3D visualizationenrichrecording
embodiflow/episode-success@1Episode successenrichepisode
embodiflow/filter-episodes@1Filter episodescuratedataset version
embodiflow/hand-mano@1Hand pose (MANO)enrichrecording
embodiflow/hand-pose@1Hand poseenrichrecording
embodiflow/lerobot-export@1LeRobot v3 exportexportdataset version
embodiflow/lerobot-preview@1Video previewenrichrecording
embodiflow/propose-episodes@1Propose episodessegmentrecording
embodiflow/raw-metrics@1Raw-file metricscheckrecording
embodiflow/recording-episodes@1Recording episodessegmentrecording
embodiflow/sensor-calibrate@1Sensor calibrationenrichrecording
embodiflow/split-episodes@1Split episodescuratedataset version
embodiflow/stereo-depth@1Stereo depthenrichrecording
embodiflow/visual-metrics@1Visual metricscheckrecording

Input and Output nodes

What the two ends do depends on the workflow type:

Workflow typeInput givesOutput takesWhat Output does
processingrawrecording (required), features[], checks[], episodesMerges features into the recording, stores checks and evaluates the quality rules, creates episodes from episodes (otherwise one per recording) without touching episodes that people made, and writes privacy evidence after an anonymizing transform.
curateversionselection[], at least oneCombines the selections and creates the new dataset version, linked to the source version and the run.
exportversionartifact (required)Stores the files and offers the download.

The Output node runs as steps you don't place but can see in the run view: Merge features (added only when features are connected), then saving the result, evaluating quality per recording, and reporting once per run. A recording is saved exactly when the saving step succeeded. Reading the upload and its facts happens before the Input node. Two steps that write a feature with the same name are refused with COMPOSE_FEATURE_CONFLICT.

Connecting steps

The editor, saving and running all apply the same rules, so a workflow that looks fine in the editor won't surprise you at run time. Every refusal says why.

RuleError
The data type is equal.WORKFLOW_PORT_INCOMPATIBLE
The content fits: every requires entry of the input is declared by the producer.WORKFLOW_CONTENT_MISSING
There are no cycles.WORKFLOW_CYCLE
Every required input is connected. An output may feed any number of inputs.WORKFLOW_INPUT_UNWIRED
Only the Output node's features, checks and selection take several connections. Every step input takes exactly one.Refused, with the reason
A processing workflow's output.recording traces back to exactly one standardize step.WORKFLOW_NO_STANDARDIZE
An episode-level step's episodes input and output.episodes come from the same producer.WORKFLOW_EPISODES_MISMATCH

Content is checked before any run, not just the type. A features output carries the features its algorithm declares; a recording output carries its producer's content plus every transform's declared change; an episodes output carries the declared labels. In the example above, hand_pose needs calibration.stereo and calibrate declares it, so that connection is allowed.

Conditions

when makes a step conditional. It's a list of conditions, and all of them must hold:

ConditionExample
A fact{ fact: duration_s, op: ">", value: 60 }
A run parameter{ param: run_depth, op: "==", value: true }
A check result{ check: pre_check.frame_gap_ratio, status: [pass, warn] }. The step waits for that check.

op is one of ==, !=, >, >=, <, <= and in. When a condition is false, the step ends as skipped (condition false), which counts like not applicable further down. That's different from an algorithm's own requires, which ends the step as not applicable when the data lacks what it needs.

When a step doesn't succeed

There is one rule behind all of this: a step runs when every required input has a value. Everything else follows from it.

  • A step that fails or is not applicable produces nothing.
  • If a required input is missing because its producer was not applicable, the step is not applicable too.
  • If a required input is missing because its producer failed, the step is skipped with "upstream failed", naming the producer.
  • An optional input whose producer didn't succeed is simply absent.
  • In a processing workflow, recording is required at the Output node, while features, checks and episodes are optional. So a failed hand-pose step still leaves the recording saved, just without hand pose: the recording succeeds, the run is partially succeeded and the failed step stays red.

Stopping checks

Set onFail: stop on a check step to make it a gate. When any result of that check is fail, the recording stops: steps that haven't started end as skipped (stopped by check), and nothing is saved (WORKFLOW_STOPPED_BY_CHECK). warn, error, not_evaluated and not_applicable never stop a recording, and they never count as a pass either.

  - id: pre_check
    uses: embodiflow/raw-metrics@1
    inputs: { raw: input.raw }
    onFail: stop
Put stopping checks early, on raw. Every step that isn't upstream of a stopping check waits for it, so an early gate saves the most work.

Episode-level steps

A check or enrich step with level: episode runs once per episode of its episodes input, in parallel: at most 4 at a time and at most 100 episodes in all (WORKFLOW_FAN_OUT_TOO_LARGE above that).

One failed episode doesn't fail the others. The step succeeds when at least one episode succeeded, fails only when every episode failed, and keeps the count "N of M episodes failed"; the run is partially succeeded and lists the failed episodes. A step with failed episodes isn't cached, so a rerun tries those episodes again. With no episodes, the step is skipped, which counts like not applicable, so the Output node still saves.

An episode-level stopping check never stops the whole recording; it only drops the failing episode from what is saved. The checks of an episode-level step can't be connected to the Output node's checks, but its features still reach the saved recording through the merge. In the example, success takes propose.episodes, and the same episodes are the workflow's output.

Privacy

In a processing workflow, every path from a standardize step to anything that reads a recording, and to output.recording, passes through the built-in anonymize step. Checks and built-in algorithms whose outputs are numbers only are the exceptions. A path that skips it is refused with WORKFLOW_PRIVACY_BARRIER. When a recording doesn't require privacy protection, anonymize hands it through unchanged, so one workflow serves both cases.

Team code that reads raw files, or a recording that isn't anonymized yet, only runs for people who may read raw recordings; anyone else is refused with WORKFLOW_RAW_CODE_AUTHORITY_DENIED.

Curate and export workflows

A curate workflow starts from a dataset version and ends with a new one. The selection ports of its steps all connect to the Output node, and an episode is kept only if every selection keeps it.

apiVersion: embodiflow-workflow/v1
name: Curate standard
description: Drop episodes by quality and duration, then split the rest into train and validation by session.
type: curate
steps:
  - id: filter
    uses: embodiflow/filter-episodes@1
    inputs: { version: input.version }
  - id: split
    uses: embodiflow/split-episodes@1
    inputs: { version: input.version }
output:
  selection: [filter.selection, split.selection]

An export workflow starts from a dataset version and ends with files.

apiVersion: embodiflow-workflow/v1
name: Export LeRobot v3
description: Export the dataset version as a LeRobot v3 dataset.
type: export
steps:
  - id: export
    uses: embodiflow/lerobot-export@1
    inputs: { version: input.version }
output:
  artifact: export.artifact

Run them on a version

On a dataset version page, Curate and export lists the project's curate and export workflows. Choose Run to start the curate workflow on that version. The run reports how many episodes it kept and dropped and links to the new version, or tells you no new version was needed. Export workflows aren't started there; use the version's export action instead.

  • A curate run creates a new, immutable dataset version. The source version is never changed, and the new version records its source and the run.
  • Starting or rerunning a curate run by hand needs permission to run workflows and to write data in the project. An export workflow needs permission to run workflows and to write exports.
  • A project binds one curate workflow. Turn on Run when a version is created and every new version starts the curate workflow, then the export workflow. A version that a curate run created doesn't start the same curate workflow again, so there's no loop, and a workflow that can't start never blocks the other.
  • A workflow with problems, or an unpublished revision, can't be bound or run.

Built-in templates

Templates are read-only. To change one, copy it with Duplicate to edit.

TemplateTypeSteps
robot-standard (Robot standard)processingRaw-file metrics that can stop the run, standardize, Anonymize, Visual metrics, and Recording episodes, which saves the recording's own episodes by a rule (no model): none for a continuous recording, a delivery's bound episodes, else the whole recording.
human-ego-standard (Human egocentric standard)processingRaw-file metrics that can stop the run, standardize, Sensor calibration and Anonymize, then Hand pose, Head pose (visual-inertial) and Stereo depth, Visual metrics, Propose episodes and Episode success per episode.
curate-standard (Curate standard)curateFilter episodes and Split episodes.
export-lerobot-v3 (Export LeRobot v3)exportLeRobot v3 export.

Robot standard

apiVersion: embodiflow-workflow/v1
name: Robot standard
description: Pre-check the upload, standardize it, anonymize when the recording requires privacy, measure visual quality, and save the recording's own episodes.
type: processing
params:
  task: { type: string, default: "", title: Task text }
steps:
  - id: pre_check
    uses: embodiflow/raw-metrics@1
    inputs: { raw: input.raw }
    onFail: stop
  - id: standardize
    uses: auto
    inputs: { raw: input.raw }
    with: { task: "${{ params.task }}" }
  - id: anonymize
    uses: embodiflow/anonymize@1
    inputs: { recording: standardize.recording }
  - id: visual
    uses: embodiflow/visual-metrics@1
    inputs: { recording: anonymize.recording }
  - id: recording_episodes
    uses: embodiflow/recording-episodes@1
    inputs: { recording: anonymize.recording }
    with: { task: "${{ params.task }}" }
output:
  recording: anonymize.recording
  checks: [pre_check.checks, anonymize.checks, visual.checks]
  episodes: recording_episodes.episodes

Next

  • Command line
  • Runs
Previous

Image algorithms

Next

Interface reference