Core concepts · Workflows

Workflows

A workflow is the versioned definition a run executes. It declares what it accepts, what it does, which models it uses, what has to pass, and what it produces. You do not write prompts against Esy — you pick a workflow and fill in its intake.

Workflow, or template?
Both. The object is a workflow and lives at /v1/workflows; the field you pass when starting a run is templateId. Same thing, two names, and the field name is not going to change.

The contract

Every workflow is a contract with two ends. intakeSchema declares what goes in; artifactSchema declares what comes out. Between them sit the steps, the bindings, and the gates.

GET /v1/catalog/workflows/generate-coloring-pagejson
{
  "id": "generate-coloring-page",
  "name": "Generate Coloring Page",
  "artifactClass": "visual",
  "outputType": "visual",
  "status": "active",
  "supersededById": null,
  "version": "2026.09.10",

  "intakeSchema":  { "fields": [ … ] },
  "runtimeSteps":  [ … ],
  "gates":         [ … ],
  "providers":     { "imageGenerator": "…", "classifier": "…", "textGate": "…" },
  "budgetPolicy":  { "perRunCapUsd": 0.35 },
  "artifactSchema": {
    "artifactClass": "visual",
    "artifactType": "coloring-page",
    "files": ["image/png"],
    "metadata": { "aspectRatio": "enum [3:4, 1:1, 4:3]", "classification": "object" }
  }
}
FieldTypeRequiredDescription
idstringrequiredStable slug, and the value you pass as templateId. Never reused for a different workflow.
artifactClassenumrequiredvisual, research, video, or knowledge.
versionstringrequiredA date-like label such as 2026.09.10. Runs pin this. See Versioning.
statusenumoptionalactive or deprecated. A deprecated workflow refuses new runs and names its successor.
intakeSchemaobjectrequiredThe fields this workflow accepts. See Intake.
runtimeStepsarrayrequiredThe ordered program. See Runs and steps.
gatesarrayoptionalWhat must pass, and which step each gate unlocks. See Gates and review.
providersobjectoptionalRole → model-registry id. The bindings a run may override.
budgetPolicyobjectoptionalAdvisory, chiefly perRunCapUsd. Real enforcement comes from budgets.
artifactSchemaobjectrequiredThe declared output: class, type, file types, and promised metadata.

Finding one to run

The catalog is public and needs no key, which makes it the right thing to point a build script at.

GET /v1/catalog/workflowsbash
curl -s https://api.esy.com/v1/catalog/workflows

The list gives you the summary of each workflow — what it is for, roughly how long it takes, whether it includes QA, what you provide and what you get. To see the actual input contract, ask for one by id:

GET /v1/catalog/workflows/{id}bash
curl -s https://api.esy.com/v1/catalog/workflows/generate-coloring-page
Prompts and bindings are redacted in the catalog
The public contract shows you the shape — fields, steps, gates, outputs — but the prompt templates and the exact model ids come back as "redacted". You get everything you need to call the workflow correctly, and nothing you would need to clone it.

Naming

Ids are verb-noun in kebab case, and the verb is load-bearing: it tells you what kind of work happens, which is usually what you actually want to know when choosing between two workflows.

VerbMeansExample
generateInvents something new from a description.generate-illustration
planResearches and produces a structured plan, not the thing itself.plan-clipart-pack
buildComputes a result from inputs, deterministically where it can.build-…
composeWrites from supplied sources rather than inventing.compose-…
editChanges an existing artifact you supply.edit-image
removeTakes something away from an input.remove-image-background
transcribeConverts between media, preserving content.transcribe-…

assemble is reserved for an internal stage and never appears in an id. create and make are deliberately unused — they say nothing that generate or build does not say more precisely.

Visibility

Not every workflow is runnable, and the ladder is strict. A workflow must be at least internal before a run against it will execute.

VisibilityIn the catalog?Runnable?
draftNoNo — creating a run does not execute it
internalNoYes
publicYesYes

Validating a published workflow

For a workflow in the catalog, validation happens on the server the moment you call it: POST /v1/runs checks your intake against the intakeSchema and returns a 422 naming every bad field before a provider is called. A rejected run costs nothing, so the cheapest pre-check is to read the schema, build the intake from it, and let the server have the final word. See Errors.

Validating a workflow you are writing

POST /v1/workflows/dry-run is an authoring tool. It does not take a templateId — you send a draft definition (its steps, bindings, and intake schema) plus a sample intake, and it resolves the plan and prices it without calling a provider.

POST /v1/workflows/dry-runbash
curl -X POST https://api.esy.com/v1/workflows/dry-run \
  -H "Authorization: Bearer $ESY_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "intakeSchema": { "fields": [ … ] },
    "runtimeSteps": [ … ],
    "providers":    { "imageGenerator": "openai/gpt-image-2-2026-04-21" },
    "intake":       { "prompt": "a lighthouse at dusk" }
  }'
200 OKjson
{
  "ok": true,
  "stepCount": 3,
  "steps": [ … ],
  "errors": [],
  "warnings": [],
  "estimate": {
    "estimatedCost": { "min": 0.004, "typical": 0.008, "max": 0.012, "currency": "USD" },
    "highCost": false
  }
}
Sending a templateId does nothing
A body of { "templateId": …, "intake": … } is accepted and silently ignored — you get back ok: true with zero steps and the warning “template has no runtime steps”. That is not a pass. Check stepCount and warnings, not just ok.

POST /v1/workflows/estimate works the same way — it prices a draft runtimeSteps + providers pair, not a published workflow. Because the public catalog redacts prompts and bindings, you cannot dry-run or estimate someone else’s workflow; you can only call it.

On “workflow schema”
You may see schemaVersion: "workflow-schema-v1" on a workflow. It is a label, not a registry: there is no v2, and no separate validator keyed to it. The validation that actually runs is the dry-run above. Treat the field as provenance, not as a contract you can look up.
In short
  • A workflow declares both ends of its contract: intakeSchema in, artifactSchema out.
  • The catalog is public and unauthenticated — read the contract before you call, rather than guessing field names.
  • The verb in the id tells you what kind of work it does. generate invents; compose writes from sources.
  • A bad intake is rejected with a 422 before anything is spent. Dry-run and estimate are for workflows you are authoring, and take a definition rather than a templateId.