Skip to content

Presets

Presets: named, versioned lists of steps — atomic ops and processing-registry steps — saved once and run by slug, id or slug@version

7 endpoints under /api/v1/presets. What every call shares is on the REST overview, and what they are for — with the calls for every SDK, the CLI and MCP — on Presets. * marks a required field.

List presets

GET /api/v1/presets

Built-in presets first, then your own, most recently updated first. Each entry is the current version — what the preset runs now — and usage, how much it is being run.

NOT PAGED: the built-ins and every preset of yours (an account holds at most 1000) come back in one answer, with no meta.

NO HISTORY BY DEFAULT: versions is left off, and versionCount says how many there are (the current one plus every superseded one). Read one preset's snapshots with GET /api/v1/presets/{slug}, or pass ?includeVersions=true here for the export shape of every preset at once — which is what POST /api/v1/presets/import takes back.

USE THIS WHEN:

  • Choosing a preset to run, or listing what an account has saved

DO NOT USE WHEN:

  • You know the slug → GET /api/v1/presets/{slug}

FILTER: omit for both, builtin for the shipped ones only, user for your own only.

Parameters

NameInTypeDescription
filterquerystringFilter by source: 'builtin' or 'user'. Omit to return all.one of builtin · user
includeVersionsquerybooleanInclude each preset's superseded versions — the export shape. Off by default: a list row is read to choose a preset, and its history is up to 50 snapshots per preset.

Returns 200 — data is PresetData[]

Every preset the filter selects, built-ins first

Errors

StatusCode and when
400

invalid_param — filter is neither builtin nor user

Create a preset

POST /api/v1/presets

Accepts an Idempotency-Key

Saves version 1 and answers 201 with the preset as saved. Each step is {op, model?, prompt?, parameters?} — an op from GET /api/v1/ops — or {operation, params} — a processing-registry key, for what no op covers. Steps run in order, and they are checked when written, so a preset that saves is one a job can run (the 400s below name the step).

ANSWERS WITH warnings (advisory, the preset is saved): what the steps say that is legal and probably not meant — a format a later step shadows, a step the one before it already did.

NOT REFUSED: an AI step beside other steps, or several AI steps. Such a preset runs as one chain job, each item walking its segments in order (POST /api/v1/jobs).

LIMITS: at most 1000 presets per account, and 50 versions on record per preset (both 422 resource_limit_exceeded); built-ins do not count.

SLUG: generated from the name when absent (a numeric suffix keeps it unique); one you send must be free, lowercase letters, digits and hyphens, and not start with builtin-.

CONSISTENCY (subjects): each subject is {name, referenceAssetIds, descriptor}. The images ride along on the generate / edit step and pin the geometry; the descriptor expands into its prompt wherever you write {{subject.<name>}} (/docs/presets). At most 4 subjects and 4 images across all of them, each your own and DONE; a subject with no images is refused. A name is a handle ([a-z0-9][a-z0-9_-]{0,39}, unique in the preset), a descriptor at most 300 characters.

Request body — PresetData

Returns 201 — data is PresetData

The preset as saved, with warnings when there is something to say

Errors

StatusCode and when
400
  • invalid_param on name — missing or blank
  • invalid_param on steps — no steps
  • invalid_param on steps[i] — a step with both or neither of op / operation
  • invalid_param on steps[i].op — an unknown op, or one that is not a step (read_metadata, render_template)
  • invalid_param on steps[i].op — an op that answers with JSON (analyze) anywhere but last: nothing after it has an image
  • invalid_param on steps[i].op — an op that takes no input image (generate) in a preset of more than one segment
  • invalid_param on steps[i].parameters.<name> — a parameter outside the op's contract
  • invalid_param on steps[i].parameters.<name> — frame / metadata / density given two values in one pass, or frame=all where the pass writes a still format
  • invalid_param on steps[i].model — a model no provider runs or one that does not run the step's op; on a deterministic step, any model (steps[i].prompt likewise)
  • invalid_param on steps[i].operation — an unknown registry operation; steps[i].params… — arguments it refuses
  • invalid_param on subjects — see CONSISTENCY above: too many subjects or images, an image that is not your own DONE asset, a bad or repeated name, a long descriptor, or no generate / edit step to send them with
  • invalid_param — the slug you sent is malformed, reserved or already one of yours
422

resource_limit_exceeded — the account already holds 1000 presets

Import presets

POST /api/v1/presets/import

Accepts an Idempotency-Key

Takes the list GET /api/v1/presets?filter=user&includeVersions=true returns — without includeVersions the rows carry no history — or any list of preset documents. version and versions are kept as exported (version defaults to 1), so an imported preset replays its history. Each entry is checked like a create; one that fails is reported in errors and the rest still land, so a 201 can carry refusals — read errors. A slug already in use gets a numeric suffix instead of being refused.

LIMITS: the whole import is refused (422 resource_limit_exceeded) if it would take the account past 1000 presets; an entry carrying more than 50 versions is reported in errors and the rest still land.

Request body — PresetData[]

Returns 201 — data is BatchImportResultDTOPresetData

What landed, and one line per entry that did not

Errors

StatusCode and when
400

invalid_param — the body is not a non-empty list

422

resource_limit_exceeded — the entries would take the account past 1000 presets; nothing was imported

Get a preset, or one earlier version of it

GET /api/v1/presets/{slug}

{slug} is a slug or id for the current version, or slug@version for one version — the spelling POST /api/v1/jobs {presetId} and POST /api/v1/images/transform?preset= accept, so what a job ran can always be read back. ?version=N is the same as slug@N; give the version one way, not both.

The response is the export shape: steps, subjects, version and the superseded versions, plus usage over every version. An earlier version comes back with its own steps, subjects and updatedAt (when that version was saved) and without history.

Parameters

NameInTypeDescription
slug (required)pathstringPreset slug or id, or slug@version
versionqueryinteger (int32)Read an earlier version (1 … current); the same as slug@N

Returns 200 — data is PresetData

The preset, at the version asked for

Errors

StatusCode and when
400
  • invalid_param on slug — the version (@N or ?version=N) is not a positive number
  • invalid_param on version — the version is given both as @N and as ?version=
404

preset_not_found — no such preset, no such version of it (never saved, or deleted), or it is not yours

Update a preset — a new version when its steps or subjects change

PUT /api/v1/presets/{slug}

Accepts an Idempotency-Key

Send only what changes: the body is merged over the current preset, and a field it leaves out keeps its value ({"name": "…"} is a whole rename; the slug stays unless you send one). What you do send is checked exactly as on create; "" empties the description. A change to steps or subjects moves the preset to version + 1 and keeps the previous one readable and runnable as slug@version, so a job or automation that pinned a version keeps running exactly that. A name, description or slug edit is not a version. Sending the steps it already has changes nothing. Answers with the preset as saved, and warnings as a create does.

DO NOT USE WHEN:

  • Changing a built-in (403) → GET it and POST its steps as your own

Parameters

NameInTypeDescription
slug (required)pathstringPreset slug or id

Request body — PresetData

Returns 200 — data is PresetData

The preset as saved — at version + 1 when its steps or subjects changed

Errors

StatusCode and when
400
  • invalid_param on slug — the path names a version (slug@N): a version cannot be changed, the next one is saved instead
  • invalid_param on name, steps[i]… or subjects — what you sent fails a create's check (see POST /api/v1/presets)
  • invalid_param — a new slug that is malformed, reserved or already one of yours
403

forbidden — a built-in preset; GET it and POST its steps as your own

404

preset_not_found — no such preset, or it is not yours

422

resource_limit_exceeded on steps — the preset already keeps 50 versions; delete one nothing pins (DELETE /api/v1/presets/{slug}/versions/{version}) or save the steps as a new preset

Delete a preset

DELETE /api/v1/presets/{slug}

Accepts an Idempotency-Key

Deletes the preset and every version of it; every slug@N a caller holds answers 404 preset_not_found from then on. A job that already ran keeps its own copy of what it ran. Built-ins are 403.

Parameters

NameInTypeDescription
slug (required)pathstringPreset slug or id

Returns 204 — no body

Deleted; no body

Errors

StatusCode and when
400

invalid_param on slug — the path names one version (slug@N); that is DELETE /api/v1/presets/{slug}/versions/{version}

403

forbidden — a built-in preset

404

preset_not_found — no such preset, or it is not yours

Delete one superseded version

DELETE /api/v1/presets/{slug}/versions/{version}

Accepts an Idempotency-Key

Drops slug@version from the preset's history. After it, that reference is 404 preset_not_found for good — nothing else changes: the preset keeps running whatever version it ran, the numbering is untouched (the next version is always current + 1, so a deleted number is never reissued), and a job that ran this version keeps its own copy of what it ran.

USE THIS WHEN:

  • A preset is at its version ceiling (422 resource_limit_exceeded on PUT) and you know which earlier versions nothing pins
  • A stored version carries a prompt you want gone

DO NOT USE WHEN:

  • Anything might still call slug@version — a pinned reference stops resolving, and that is the whole cost of this call

To move what an UNPINNED call runs, PUT the old steps as a new version instead.

Parameters

NameInTypeDescription
slug (required)pathstringPreset slug or id — without an @, the version is the path segment below
version (required)pathinteger (int32)The superseded version to delete

Returns 204 — no body

Deleted; no body

Errors

StatusCode and when
400
  • invalid_param on version — the current version (save the next one first, or delete the whole preset), or a number below 1
  • invalid_param on slug — the slug carries an @version of its own
403

forbidden — any version of a built-in preset

404

preset_not_found — no such preset, or no such version on record (never saved, or already deleted)

Objects

Each described once; a linked type is another object on this page.

BatchImportResultDTOPresetData

What an import did: the entries that landed, and one line for each that did not

FieldTypeDescription
dataPresetData[]
The created resources, as saved, in the order sent
errorsstring[]
One line per refused entry, naming its index and why (Preset at index 2 'Hero': …). Absent when every entry landed.
importedCountinteger (int32)
How many entries were created — the length of data

PresetData

A preset: a named, versioned list of steps, each an L1 op or a processing-registry step. GET returns the export shape; POST /api/v1/presets/import takes it back.

FieldTypeDescription
builtInboolean
Whether this is a built-in preset (read-only)
createdAtstring (date-time)
When the preset was created
descriptionstring
Free text for people; not run. "" on a PUT empties it.
idstring
Preset id (pre_…; a built-in's starts with builtin-). A job's presetId takes it, the slug, or either with @version.
namestring
Display name (required on create).e.g. "Product cut-out, 1200 WebP"
slugstring
URL-safe handle, unique per account: lowercase letters, digits and hyphens. Generated from the name when absent (a numeric suffix keeps it unique); one you send must be free. builtin- is reserved.e.g. "product-cutout"
stepsPresetStep[]
The steps, run in order. Each is {op, model?, prompt?, parameters?} (an op from GET /api/v1/ops) or {operation, params} (a processing-registry key, for what no op covers). Checked when written: a parameter outside its op's contract is 400 invalid_param on steps[i].parameters.<name>. Adjacent deterministic steps run as one process pass and each AI step as its op's own job; those are the preset's segments. One segment runs as that job; more than one — an AI step beside other steps, or several AI steps — runs as one chain job, each item walking the segments in order with the output of one as the input of the next.
subjectsSubject[]
What has to look the same every time: each subject is {name, referenceAssetIds, descriptor}, at most 4 of them. The images (max 4 across all subjects, your own, status DONE) ride along on the preset's generate / edit step and pin the geometry; the descriptor expands into its prompt wherever you write {{subject.<name>}}. A preset with subjects needs a generate or edit step.
updatedAtstring (date-time)
When the preset was last saved, a rename included — the order your own rows are listed in. On an earlier version read as slug@N, when that version was saved.
usagePresetUsage
How often this preset has run and when it last did, over the jobs still on record (imagestep#423). Present on reads — GET /api/v1/presets and GET /api/v1/presets/{slug} — and left off a write, which answers about what it just saved. Derived, never stored.
versioninteger (int32)
Starts at 1 and goes up whenever steps or subjects change; a name, description or slug edit is not a version. Run or read an earlier one as slug@version / ?version=N.e.g. 3
versionCountinteger (int32)
How many versions are on record: the current one plus every superseded one. Always present on a read, whether or not versions is, so a list reader can tell a preset with history from one without fetching it.e.g. 3
versionsPresetVersion[]
Superseded versions, oldest first. Present in exports so an import replays the same history. GET /api/v1/presets leaves it off unless you ask for ?includeVersions=true — a list row does not need every snapshot of every preset, and versionCount says whether there is any history to read.
warningsStepWarning[]
What these steps say that is legal, will run, and is probably not what was meant (imagestep#416): a format a later step shadows, a step the one before it already did. Advisory — the preset was saved. Derived, never stored: a write answers with the findings about what it just saved, and POST /api/v1/jobs?dryRun=true recomputes them for any preset. What could not run at all was refused instead, 400 invalid_param naming the step.

PresetStep

A preset step: either an L1 op from GET /api/v1/ops — {op, model?, prompt?, parameters?} — or a processing-registry step — {operation, params} — for what no op covers. Never both.

FieldTypeDescription
modelstring
AI steps: the model; the op's default when absent.
opstring
An op from GET /api/v1/ops (kind ai or deterministic).e.g. "resize"
operationstring
A processing-registry key (deterministic only), for steps no op covers.e.g. "sharpen"
parametersobject
The op's parameters. A deterministic op's are checked against its catalogue contract when the steps are written, and so are analyze's bounds; an AI image op's go to the model as sent.
paramsany
That registry step's arguments.
promptstring
AI steps: the prompt. {{subject.<name>}} expands to that subject's descriptor.

PresetUsage

How often this preset has been run, over the jobs still on record — job records expire with the assets they produced, so this is the retention window and not all time. Read-only and derived.

FieldTypeDescription
lastRunAtstring (date-time)
When the most recent of them was submitted.e.g. "2026-09-21T09:14:22Z"
runsinteger (int64)
Jobs that ran this preset, at any version, within the retention window.e.g. 12

PresetVersion

A superseded version of a preset: its steps and subjects as they were, and when that version was saved.

FieldTypeDescription
stepsPresetStep[]
The steps as that version had them.
subjectsSubject[]
The subjects as that version had them.
updatedAtstring (date-time)
When that version was saved.
versioninteger (int32)
The version number; slug@version runs and reads exactly this.

StepWarning

An advisory finding about a list of steps: legal, and probably not what was meant

FieldTypeDescription
codestring
Machine-readable finding code from a closed set; the table is /docs/presets#warningse.g. "format_shadowed"
messagestring
The finding in one sentence, naming the steps by indexe.g. "steps[1] writes jpeg and steps[3] writes webp; a process segment writes one image, in the format of its last format step, so steps[1]'s format is not written"
stepsinteger (int32)[]
The step indexes the finding is about, in order — the redundant one firste.g. [1,3]

Subject

A recurring character, product or location: reference images plus the locked words for it.

FieldTypeDescription
descriptorstring
The locked description — colour, material, markings — at most 300 characters. Text pins what an image cannot show.
namestring
Lowercase handle ([a-z0-9][a-z0-9_-]{0,39}, unique among the subjects), referenced in a prompt as {{subject.<name>}}.e.g. "hero"
referenceAssetIdsstring[]
Your own DONE asset ids showing this subject. They pin the geometry.