---
title: "Create a preset"
method: POST
path: "/api/v1/presets"
tags: ["Preset Management"]
---

# Create a preset

`POST /api/v1/presets`

## Request

- Base URL: `https://api.imagestep.dev`
- URL: `https://api.imagestep.dev/api/v1/presets`
- Auth: API key in header `Authorization`

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.

## Headers

- `Idempotency-Key` string

## Request body

- 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.
  - `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).
  - `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.
  - `description` string: Free text for people; not run. `""` on a PUT empties it.
  - `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.
    - `name` string: Lowercase handle (`[a-z0-9][a-z0-9_-]{0,39}`, unique among the subjects), referenced in a prompt as {{subject.<name>}}.
    - `referenceAssetIds` string[]: Your own DONE asset ids showing this subject. They pin the geometry.
    - `descriptor` string: The locked description — colour, material, markings — at most 300 characters. Text pins what an image cannot show.
  - `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.
    - `op` string: An op from GET /api/v1/ops (kind ai or deterministic).
    - `model` string: AI steps: the model; the op's default when absent.
    - `prompt` string: AI steps: the prompt. {{subject.<name>}} expands to that subject's descriptor.
    - `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.
    - `operation` string: A processing-registry key (deterministic only), for steps no op covers.
    - `params` object: That registry step's arguments.
  - `version` integer: 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`.
  - `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.
    - `version` integer: The version number; `slug@version` runs and reads exactly this.
    - `steps` PresetStep[]: The steps as that version had them.
      - `op` string: An op from GET /api/v1/ops (kind ai or deterministic).
      - `model` string: AI steps: the model; the op's default when absent.
      - `prompt` string: AI steps: the prompt. {{subject.<name>}} expands to that subject's descriptor.
      - `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.
      - `operation` string: A processing-registry key (deterministic only), for steps no op covers.
      - `params` object: That registry step's arguments.
    - `subjects` Subject[]: The subjects as that version had them.
      - `name` string: Lowercase handle (`[a-z0-9][a-z0-9_-]{0,39}`, unique among the subjects), referenced in a prompt as {{subject.<name>}}.
      - `referenceAssetIds` string[]: Your own DONE asset ids showing this subject. They pin the geometry.
      - `descriptor` string: The locked description — colour, material, markings — at most 300 characters. Text pins what an image cannot show.
    - `updatedAt` string, date-time: When that version was saved.
  - `versionCount` integer: 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.
  - `createdAt` string, date-time: When the preset was created
  - `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.
  - `builtIn` boolean: Whether this is a built-in preset (read-only)
  - `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.
    - `code` string: Machine-readable finding code from a closed set; the table is /docs/presets#warnings
    - `message` string: The finding in one sentence, naming the steps by index
    - `steps` integer[]: The step indexes the finding is about, in order — the redundant one first
  - `usage` 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.
    - `runs` integer: Jobs that ran this preset, at any version, within the retention window.
    - `lastRunAt` string, date-time: When the most recent of them was submitted.

## Response `201`

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

## Other responses

- `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 or has a step that draws on the finished picture — a mask, a region blur, a watermark or a caption - `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
- `409`: Conflict — `error.code` is `idempotency_key_reuse` (the key already means a different request) or `request_in_progress` (the first request under this key has not settled yet; retryable).
- `422`: `resource_limit_exceeded` — the account already holds 1000 presets
- `429`: Rate limited — `error.code` is `rate_limited` and `retryable` is true. `Retry-After` says how many seconds to wait.

---

[API](https://skmtc.dev/imagestep/apis/imagestep-api.md) · [All operations](https://skmtc.dev/imagestep/apis/imagestep-api/llms.txt) · [OpenAPI document](https://skmtc.dev/imagestep/apis/imagestep-api/revisions/5e8e922a3085?raw)
