Core concepts · Intake

Intake

Intake is what you hand a run. Each workflow declares the fields it accepts, and Esy validates your payload against that declaration before a single provider call is made — so a typo costs you a 422 rather than a render.

Reading the schema

Fetch any workflow’s contract and you get its intakeSchema: a flat list of fields, each with a type, a required flag, and — where the field is constrained — its allowed values and default.

intakeSchema, from generate-coloring-pagejson
{
  "fields": [
    {
      "name": "prompt",
      "type": "string",
      "required": true,
      "description": "Subject of the coloring page"
    },
    {
      "name": "aspectRatio",
      "type": "enum",
      "required": true,
      "default": "3:4",
      "options": ["3:4", "1:1", "4:3"]
    },
    {
      "name": "quality",
      "type": "enum",
      "required": false,
      "default": "medium",
      "options": ["low", "medium", "high", "xhigh", "max"]
    }
  ]
}

Field types you will meet:

TypeMeaning
stringFree text. Usually the prompt or a title.
enumOne of a fixed list in options. Anything else is rejected.
booleanA switch, almost always with a sensible default.
integerA count.
numberA decimal, typically a threshold.
arrayA list of values.
objectStructured input for a step that needs more than a scalar.
artifactReferenceThe id of an existing artifact to feed into this run.
Defaults are applied, not assumed
A field with a default is filled in for you when you omit it, and the value that was used is recorded on the run. You never have to send a field just to get its default, and you can always see afterwards what the run actually ran with.

When it does not validate

Every problem is named, so you should never have to guess which field was wrong. This is the single most common error against the API.

POST /v1/runsjson
HTTP 422

{
  "detail": {
    "error": "intake_invalid",
    "fields": [
      "intake.prompt is required",
      "intake.categories is required"
    ]
  }
}

Unknown keys are currently logged rather than rejected — do not rely on that as permission to send extra fields. See Errors for the full list.

Artifact inputs

Some workflows take another artifact as input — an infographic built from a research report, an edit applied to an existing image. Those fields have type artifactReference and usually constrain the artifactType they accept.

an artifactReference fieldjson
{
  "name": "sourceReport",
  "type": "artifactReference",
  "required": true,
  "artifactType": "research-report",
  "description": "The report to turn into an infographic"
}

You pass an artifact id, and Esy checks that it exists, that it is the right type, and that it is inside a workspace you can reach — before the run starts. A workflow can also declare that a step may produce the input if you do not supply one; see Compose with artifact inputs.

The rule behind the schemas

Once you have read a few intake schemas, you will notice something: they ask for results, not methods. That is deliberate, and it is the design rule that explains most of what you will otherwise find arbitrary.

Every run answers two questions, and they belong to different layers:

1 · THE INTAKE ASKS2 · THE ENGINE DECIDESWhat do you want?prompt“a fox in a waistcoat”background“see-through”aspectRatio1:1a promise. still true in five years.How should we make it?model registrysupports_native_transparencytruefalserender transparentremoval skipped · $0render, then key outesy/chroma-keyBoth record transparencyMechanism in provenance.QA judges the outcome, never the mechanism
Intake speaks outcomes, bindings speak mechanismsQuestion one is yours. Question two is the engine’s, answered from the bound model’s declared capabilities and recorded in provenance.

You ask for “a picture with a see-through background”. Whether the model renders transparency directly or Esy renders normally and keys the background out afterwards is a kitchen decision — it depends on which model is bound today and what that model can do.

Why it is built this way

A mechanism field fails twice over. First, it asks you a question already settled by the model binding, so wrong combinations simply break runs. Second, and worse, it makes saved intakes rot.

A saved intake that says…In a year
“I want a transparent background”Still exactly what you wanted. The engine picks whatever is best by then.
“Run the background-removal step”Actively worse: it forces an extra paid step that a newer model made unnecessary.
The test for a new capability

A new promise — something the workflow could not deliver before — is a template change and a new version. A better mechanism for a promise already made is not: it is a capability flag on the model, conditional logic in the step, and a provenance entry. Your saved intakes keep working and quietly get better.

Seeing what was actually used

Because the engine makes these choices, it also records them. A finished run carries the resolved intake — defaults filled in — and the artifact records the mechanism chosen, for instance transparencyMechanism: "native" versus "post-process". If you need to know why two runs of the same workflow behaved differently, that is where to look.

FieldTypeRequiredDescription
run.intakeobjectoptionalWhat you sent, with defaults applied.
run.workflowVersionstringoptionalWhich definition interpreted that intake.
run.specVersionHashstringoptionalHash of definition + intake. Two identical hashes mean identical inputs.
artifact.contentobjectoptionalThe resolved prompt, the model used, and the mechanism chosen.
In short
  • Read a workflow’s intakeSchema before calling it; it names every field, its type, and its allowed values.
  • Intake asks for outcomes. If a field looks like it is asking you how to implement something, that is a bug, not a feature.
  • Defaults are applied and recorded, so a finished run always shows what it really ran with.