Skip to main content

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 output
  • step(capabilityId, label) — invoke a capability
  • .with({…}) — step input
  • .as("echo") — write output into run state under echo
  • .run([...]) — ordered root steps (and flow nodes)
Produce a manifest in code with .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.
  • accepts property names seed ecp.run(..., { input }) and are read with ref("value").
  • returns property names are top-level state keys (typically a step .as()). After a successful run, result.output picks 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:
Field names are camelCase in JSON; capability IDs and schema literals use kebab-case segments where specified by the protocol.

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.

Next steps