Job Management

Submit a new job

Submits an async job. Name the work with op — one entry of GET /api/v1/ops — and the service derives the rest. Price it first: the same request with ?dryRun=true creates nothing.

op is the vocabulary:

  • {"op": "remove_bg", "assetIds": ["ast_…"]}
  • {"op": "resize", "assetIds": ["ast_…"], "parameters": {"width": 1200}}
  • {"op": "generate", "prompt": "a red bicycle on white", "count": 2}
  • {"op": "render_template", "templateId": "builtin-template-og-image", "items": [{"title": "Hello"}]} A parameter outside the op's contract is 400 invalid_param naming it, before any credit is touched.

presetId runs a saved preset (GET /api/v1/presets) by slug or id, or slug@version to pin one version. The preset decides the job type; model / prompt / parameters override its ONE AI step — a preset with no AI step, or with several, refuses them (400 invalid_param naming the field). steps runs the same list of steps inline, once, without saving a preset (op, presetId and the overrides are refused beside it — put them on the step). assetIds batches: one item per asset. Several segments — an AI step beside other steps, or two of them — run as one chain job, each item walking the segments in order; the dry run breaks its price down per segment (steps[]). type (its values are the enum on the request schema) is what an op or a preset translates into; send it only without op.

The response is the job: follow it with GET /api/v1/jobs/{id} or a job.* webhook. Nothing is charged at submit — the credits of an AI job are taken when it settles, for the items that completed. A deterministic job counts its items against the period's processing allowance instead.

wait (seconds, at most 60; a larger value is clamped) holds the response until a ONE-item job is terminal: 200 with the finished job and its outputs, or 202 with the same job when the window closes first — then GET /api/v1/jobs/{id}?wait= waits again. One call, one result, and still a job: it is charged for what completes, retried and replayable exactly as without it. On a batch wait is ignored and the answer is the 201 at once.

The dry run is a validation pass. It answers every 400 and 404 a submit would, but never refuses for money or quota (402 insufficient_credit, 422 asset_count_exceeded): what the account cannot afford comes back as data instead — sufficientCredit, assetCountLeft, processCountLeft, and what runs past the allowance would cost (overageRuns, overageCredits). Images not stored yet are priced by how many: imageCount on a dry run stands for that many more assetIds — the price depends on the op, model, parameters and count, never on the pixels — and the submit after the upload names them.

Past the processing allowance a deterministic job (a pass over assets, a render, a chain's passes) is not refused: each run past it is paid from your balance at the plan's overageCredits (GET /api/v1/ops), held with the job and charged for the runs it hands out. Only a balance that cannot cover them refuses the submit — 402 insufficient_credit, like an AI job.

USE THIS WHEN:

  • Running an AI op, a batch, or anything you want an asset_id back for
  • Pricing a run before paying for it (?dryRun=true)

DO NOT USE WHEN:

  • One image you already hold and a deterministic op → POST /api/v1/images/transform (bytes in, bytes out)
  • Reading an image's metadata → POST /api/v1/images/metadata, or GET /api/v1/assets/{id} for a stored one
post/api/v1/jobs

Request

  • Base URL: https://api.imagestep.dev
  • URL: https://api.imagestep.dev/api/v1/jobs
  • Auth: API key in header Authorization

Query parameters

dryRunboolean

Price the job and return the estimate without creating it or spending anything. Accepts dryRun and dry_run. A dry run ignores Idempotency-Key, so the submit after it can carry the same one.

dry_runboolean

dryRun spelled snake_case; either one set to true is a dry run.

Headers

Idempotency-Keystring

Optional. A client-generated unique key (typically a UUID). Same key + the same request (method, path, query string and body) replays the first response verbatim with Idempotency-Replayed: true, without executing again. Same key + a different one returns 409 idempotency_key_reuse. A repeat that arrives while the first is still running returns 409 request_in_progress, which is retryable. Keys are retained 24 hours. At most 255 characters; a longer one is a 400 invalid_param with param: "Idempotency-Key".

Request body

opstring

An op name — one entry of GET /api/v1/ops, the catalogue every client enumerates. When set, type / presetId / preset are derived from it and parameters carries the op's parameters; the dry run prices it like any job.

variantsobject[]

Run the op once per entry, producing one asset each — the whole set of social sizes from one call. Each entry is {name, parameters}; the parameters are merged over the top-level ones, and each name must be distinct (it is added to the output's name). Deterministic ops only — beside an AI op, render_template or a preset it is 400 invalid_param; at most 20.

type'ai-generate' | 'ai-edit' | 'parse' | 'process' | 'render' | 'chain'

Job type. chain is what several segments run as — a preset's or inline steps — and is derived from them; asked for on its own it is refused.

templateIdstring

render_template: the template to render — an id, or id@version to pin one version. May also be given as parameters.templateId.

itemsobject[]

render_template: one object of template variables per output image (≤ 500). May also be given as parameters.items.

assetIdsstring[]

Your assets to run on — one item per id, times the variants. Required unless the op reads no image (requiresAssets: false in GET /api/v1/ops) or the job is a render. Every id must be yours: one that is not is 404 asset_not_found, before anything is created.

imageCountinteger

Dry run only (?dryRun=true): images you have not stored yet, priced as that many more assetIds — one item each, times the variants — so the price can be asked before anything is uploaded. Beside assetIds it adds to them, and those are still checked. A submit needs the images themselves as assetIds: without dryRun, or on a job that reads no image (a render), it is 400 invalid_param. At most 10000 together with assetIds.

waitinteger

Hold the response until the job is terminal, for at most this many seconds — capped at 60 (a larger value is clamped, not refused). A job of ONE item only; on a batch it is ignored and the handle comes back at once. Terminal inside the window → 200 with the finished job; still running → 202 with the same handle, and GET /api/v1/jobs/{id}?wait= waits again. Ignored on a dry run.

retentionDaysinteger

Keep the assets this job makes — and its own record — for this many days instead of your plan's retention: shorter only. More than the plan keeps is kept for the plan's time, not refused; the assets' expiresAt says what was stamped. Absent: the plan's retention.

countinteger

A job with no assetIds that makes images from a prompt: how many to make, one item each. 1 when absent.

presetIdstring

A saved preset to run: its slug or id, or slug@version / id@version to pin one version (GET /api/v1/presets). The preset decides the job type; model / prompt / parameters override its ONE AI step, and are refused by a preset that has none or several.

modelstring

The model an AI op runs on — an id from GET /api/v1/ai-models whose categories include the op's modelCategory; any other is 400 invalid_param with the ones that fit in details.allowed. Absent, the op's defaultModel. With a preset it overrides the preset's one AI step.

promptstring

The prompt of an AI op that takes one — required where the op's entry says requiresPrompt. With a preset it overrides the one AI step's prompt, and {{subject.<name>}} in it expands to that subject's descriptor.

parametersobject

The op's parameters. A deterministic op's are checked against its params in GET /api/v1/ops, and one outside them is 400 invalid_param naming it; an AI op's go to its model (parameters in GET /api/v1/ai-models). With a preset they replace its one AI step's parameters.

collectionstring

Collection to put the output assets in (optional; at most 200 characters, and names starting with job: are reserved). Without one, an output made from an asset is in that asset's collection.

Example request

{
  "op": "remove_bg",
  "type": "ai-generate",
  "templateId": "builtin-template-og-image",
  "items": [
    {
      "title": "Hello",
      "site": "imagestep.dev"
    }
  ],
  "imageCount": 20,
  "wait": 30,
  "retentionDays": 7,
  "count": 1,
  "presetId": "builtin-util-web-optimize",
  "steps": [
    {
      "op": "resize",
      "operation": "sharpen"
    }
  ],
  "subjects": [
    {
      "name": "hero"
    }
  ],
  "collection": "shoot-01"
}

Response

?dryRun=true: what the job would cost, nothing was created — or, with wait, the job, terminal inside the window (a FAILED job is terminal too: read status).

Changes

No recorded changes to this operation across all 1 revision of this API.