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
| Name | In | Type | Description |
|---|---|---|---|
| filter | query | string | Filter by source: 'builtin' or 'user'. Omit to return all.one of builtin · user |
| includeVersions | query | boolean | Include 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
| Status | Code and when |
|---|---|
| 400 |
|
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
| Status | Code and when |
|---|---|
| 400 |
|
| 422 |
|
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
| Status | Code and when |
|---|---|
| 400 |
|
| 422 |
|
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
| Name | In | Type | Description |
|---|---|---|---|
| slug (required) | path | string | Preset slug or id, or slug@version |
| version | query | integer (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
| Status | Code and when |
|---|---|
| 400 |
|
| 404 |
|
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
| Name | In | Type | Description |
|---|---|---|---|
| slug (required) | path | string | Preset 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
| Status | Code and when |
|---|---|
| 400 |
|
| 403 |
|
| 404 |
|
| 422 |
|
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
| Name | In | Type | Description |
|---|---|---|---|
| slug (required) | path | string | Preset slug or id |
Returns 204 — no body
Deleted; no body
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 403 |
|
| 404 |
|
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
| Name | In | Type | Description |
|---|---|---|---|
| slug (required) | path | string | Preset slug or id — without an @, the version is the path segment below |
| version (required) | path | integer (int32) | The superseded version to delete |
Returns 204 — no body
Deleted; no body
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 403 |
|
| 404 |
|
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
| Field | Type | Description |
|---|---|---|
| data | PresetData[] | The created resources, as saved, in the order sent |
| errors | string[] | One line per refused entry, naming its index and why ( Preset at index 2 'Hero': …). Absent when every entry landed. |
| importedCount | integer (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.
| Field | Type | Description |
|---|---|---|
| builtIn | boolean | Whether this is a built-in preset (read-only) |
| createdAt | string (date-time) | When the preset was created |
| description | string | Free text for people; not run. "" on a PUT empties it. |
| id | string | Preset id ( pre_…; a built-in's starts with builtin-). A job's presetId takes it, the slug, or either with @version. |
| name | string | Display name (required on create).e.g. "Product cut-out, 1200 WebP" |
| slug | string | 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" |
| steps | PresetStep[] | 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. |
| subjects | Subject[] | 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. |
| updatedAt | string (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. |
| usage | PresetUsage | 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. |
| version | integer (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 |
| versionCount | integer (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 |
| versions | PresetVersion[] | 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. |
| warnings | StepWarning[] | 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.
| Field | Type | Description |
|---|---|---|
| model | string | AI steps: the model; the op's default when absent. |
| op | string | An op from GET /api/v1/ops (kind ai or deterministic).e.g. "resize" |
| operation | string | A processing-registry key (deterministic only), for steps no op covers.e.g. "sharpen" |
| parameters | object | 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. |
| params | any | That registry step's arguments. |
| prompt | string | 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.
| Field | Type | Description |
|---|---|---|
| lastRunAt | string (date-time) | When the most recent of them was submitted.e.g. "2026-09-21T09:14:22Z" |
| runs | integer (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.
| Field | Type | Description |
|---|---|---|
| steps | PresetStep[] | The steps as that version had them. |
| subjects | Subject[] | The subjects as that version had them. |
| updatedAt | string (date-time) | When that version was saved. |
| version | integer (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
| Field | Type | Description |
|---|---|---|
| code | string | Machine-readable finding code from a closed set; the table is /docs/presets#warningse.g. "format_shadowed" |
| message | string | 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" |
| steps | integer (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.
| Field | Type | Description |
|---|---|---|
| descriptor | string | The locked description — colour, material, markings — at most 300 characters. Text pins what an image cannot show. |
| name | string | Lowercase handle ( [a-z0-9][a-z0-9_-]{0,39}, unique among the subjects), referenced in a prompt as {{subject.<name>}}.e.g. "hero" |
| referenceAssetIds | string[] | Your own DONE asset ids showing this subject. They pin the geometry. |