Fluent API
The Fluent API is the public authoring spec for ECP (Execution Control Protocol) workflows. You write TypeScript, compile to a JSON manifest, and run that manifest in any compatible environment. Import builders from@executioncontrolprotocol/core.
Hello workflow
workflow(label)— create a builder.accepts(schema)/.returns(schema)— JSON Schema (or Zod) for run input / public outputstep(capabilityId, label)— invoke a capability.with({…})— step input.as("echo")— write output into run state underecho.run([...])— ordered root steps (and flow nodes)
.toManifest(), or on the CLI with ecp compile.
Multi-step and refs
ref("signals.results") becomes a $ref into run state. Keep secrets out of workflows; bind credentials in the environment.
Workflow input and output
.accepts() and .returns() are the same verbs in Fluent and JSON. They describe the workflow contract — not dummy steps. Fluent render and browser compile emit JSON Schema literals (no zod import). Zod objects are an authoring convenience that serialize to the same JSON.
acceptsproperty names seedecp.run(..., { input })and are read withref("value").returnsproperty names are top-level state keys (typically a step.as()). After a successful run,result.outputpicks those keys.- Omit either field (or pass an empty object schema) when the workflow has no public I/O.
ecp.run (and --dry-run) validates input against accepts before steps execute. Invalid input throws Workflow accepts validation failed — including when dryRun: true. Extra unused keys on a valid object succeed. ecp.validate() still checks the graph and environment only.
If a required returns key is missing from state, the run finishes with run.status === "failed" and assembled output. Optional returns keys may be absent without failing the run.
The Workflow canvas shows Inputs / Outputs from these schemas. They are projections of the contract, not capabilities in steps[].
Runnable example: 07-accepts-returns.
Flow control
From@executioncontrolprotocol/core:
Compose them inside
.run([...]) alongside step(...) nodes. Step IDs are globally unique in the compiled manifest (required for patch paths).
State write modes
.as("name") can take an optional mode (for example create vs replace semantics). Prefer explicit .as() for every step whose output later steps need.
Manifest shape
Compiled workflows use:Render Fluent from a manifest
Core can encode a workflow back to Fluent source (encode path). There is no separate@executioncontrolprotocol/format-fluent npm package — Fluent encode is built into core. Fluent decode is not supported; use ecp compile for TS → JSON.