Jobs
Asynchronous work: an op, a saved preset or inline steps over a batch of assets, run as one job with a handle. Price it with a dry run, wait on it in the same call or follow it by id or webhook, cancel it, resume what failed. Credits are charged when the job settles, for what completed.
7 endpoints under /api/v1/jobs. 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 Jobs. * marks a required field.
List jobs
GET /api/v1/jobs
Paged — page and perPage, or cursor, in; meta out
Your jobs, newest first, one page at a time. Every filter is optional and they combine; a missing one filters nothing. A row carries the item counts and what ran, not items — read one job for those.
To follow a job you just submitted, read it by id — GET /api/v1/jobs/{id}?wait= holds the answer until it settles — or register a webhook. Polling this list is the slow way to watch one job.
USE THIS WHEN:
- Finding jobs by status, type, op, preset or date — last week's failures, say
- Reading every attempt of one job (
rootJobId)
DO NOT USE WHEN:
- You have the job's id →
GET /api/v1/jobs/{id} - You only need how many →
GET /api/v1/jobs/counts
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| status | query | string | Only jobs in this statusone of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED · CANCELLING |
| type | query | string | Only jobs of this typeone of ai-generate · ai-edit · parse · process · render · chain |
| preset | query | string | Only jobs that ran this preset: a slug or id for every version of it, or slug@version for the one that was pinned. A preset that is not yours, or a version that does not exist, is 404 preset_not_found. Jobs that ran inline steps or a bare op carry no preset and never match. |
| op | query | string | Only jobs submitted as this op — one entry of GET /api/v1/ops. A job submitted as a raw type or a preset carries no op and never matches. |
| rootJobId | query | string | Every attempt of one job: the ones whose rootJobId is this — the first attempt's id, which is its own rootJobId too, so it is in the page. Ordered newest first like any other page, so the latest attempt is first. |
| createdFrom | query | string | Submitted at or after this. Epoch millis, or an ISO-8601 date or instant read as UTC: 2026-09-14, 2026-09-14T08:30:00Z |
| createdTo | query | string | Submitted at or before this. Same forms as createdFrom; a bare date includes the whole of that day — which is also how to freeze the window a walk by page number pages through, since jobs you submit meanwhile would shift its pages. A walk by cursor is not shifted. |
Returns 200 — data is JobSummary[]
One page of rows, with meta
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 404 |
|
Submit a new job
POST /api/v1/jobs
Accepts an Idempotency-Key
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_idback 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, orGET /api/v1/assets/{id}for a stored one
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| dryRun | query | boolean | 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_run | query | boolean | dryRun spelled snake_case; either one set to true is a dry run. |
Request body — JobRequest
Returns
| Status | Body | When |
|---|---|---|
| 200 | data is JobEstimate or Job |
|
| 201 | data is Job | The job, created — |
| 202 | data is Job |
|
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 402 |
|
| 404 |
|
| 422 |
|
Count jobs by status and type
GET /api/v1/jobs/counts
How many of your jobs are in each status and of each type, as one table: a row per status and type that has any, with its count. Takes the filters GET /api/v1/jobs takes, except status and type — those are the table's two dimensions — and every cell is the meta.total that listing would report for that status and type.
To count by status alone, sum the rows for each status; to count the statuses within one type, keep that type's rows first.
USE THIS WHEN:
- Checking how many jobs failed, or are still running, without paging through them
- Showing a count beside each status or type filter
DO NOT USE WHEN:
- You need the jobs themselves → use GET /api/v1/jobs
- You need credits spent → use GET /api/v1/usage
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| preset | query | string | Only jobs that ran this preset, as on GET /api/v1/jobs: a slug or id for every version of it, or slug@version. A preset that is not yours is 404 preset_not_found. |
| op | query | string | Only jobs submitted as this op — one entry of GET /api/v1/ops. |
| rootJobId | query | string | Only the attempts of one job: the ones whose rootJobId is this — the first attempt's id, the first attempt included. |
| createdFrom | query | string | Submitted at or after this. Epoch millis, or an ISO-8601 date or instant read as UTC. |
| createdTo | query | string | Submitted at or before this. Same forms as createdFrom; a bare date includes the whole of that day. |
Returns 200 — data is JobCount[]
The non-zero cells; an empty list when nothing matches
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 404 |
|
Get a job
GET /api/v1/jobs/{id}
One job as it stands: its status, the item counts, what ran (op, preset and version, model, steps) and, per item, the output — resultAssetId, or output for an analyze — or, for a failed item, errorCode and retryable. The first 100 items ride inline and itemsTruncated says when there are more (GET /api/v1/jobs/{id}/items pages the rest). actualCredits is what the job was charged; it is set when the job settles.
wait turns the read into a long-poll: the answer is held until the job is terminal or the window closes, and the job as it stands comes back either way — read status. It is how a 202 from a waited submit is picked up.
A job record lives as long as the assets it made: at expiresAt it is swept, and a read after that is 404.
USE THIS WHEN:
- Reading what a job produced, or why an item failed
- Waiting for a job to finish without writing a poll loop (
?wait=)
DO NOT USE WHEN:
- Listing jobs → use GET /api/v1/jobs
- You only want the assets it made →
GET /api/v1/assets?jobId={id}
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | The job's id, job_… |
| wait | query | integer (int32) | Long-poll: hold the response until the job is terminal, for at most this many seconds (at most 60; a larger value is clamped). The job as it stands comes back either way — read status. Any job, batches included. More than 8 waits open on one account is 429 rate_limited (details.reason account_concurrency) with Retry-After; a full node is 503 provider_unavailable. Both are retryable. |
Returns 200 — data is Job
The job — terminal or not; read status
Errors
| Status | Code and when |
|---|---|
| 404 |
|
| 503 |
|
Cancel a job
POST /api/v1/jobs/{id}/cancel
Accepts an Idempotency-Key
Stops a PENDING or PROCESSING job. Items not yet handed to a provider or a worker are cancelled and never charged; items already running finish, and are charged for what they complete — nothing is taken at submit, so there is nothing to refund. The answer is the job: CANCELLED when nothing was in flight, else CANCELLING until the running items settle, and then CANCELLED. A chain item lets the segment in flight finish and starts no further one.
The job settles as CANCELLED and a job.failed event goes out. What it did make is kept; a resume retries the cancelled items.
USE THIS WHEN:
- Stopping a running or queued job you no longer want
DO NOT USE WHEN:
- The job is already terminal (
COMPLETED,FAILED,CANCELLED) orCANCELLING→400 invalid_state
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | The job's id, job_… |
Returns 200 — data is Job
The job — CANCELLING or CANCELLED
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 404 |
|
List a job's items
GET /api/v1/jobs/{id}/items
Paged — page and perPage, or cursor, in; meta out
One page of a job's items, in item order (index, from 0) — the number the rest of the API names an item by, in events and in a resume.
GET /api/v1/jobs/{id} carries the first 100 items inline and sets itemsTruncated when there are more; this is how to read the rest. A batch takes up to 10000 assets, each times its variants, so the whole list is not something to hold in a context or re-read on every poll.
USE THIS WHEN:
- A job says
itemsTruncatedand you need an item past the first page - You want only the failed ones, to decide whether a resume is worth its price (
status=FAILED)
DO NOT USE WHEN:
- You want the assets the job produced →
GET /api/v1/assets?jobId={id}, which is the run's products without the items around them
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | The job's id, job_… |
| status | query | string | Only items in this statusone of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED |
Returns 200 — data is JobItem[]
One page of items, with meta
Errors
| Status | Code and when |
|---|---|
| 404 |
|
Resume incomplete items in a job
POST /api/v1/jobs/{id}/resume
Accepts an Idempotency-Key
Retries what did not finish: a new job over the FAILED and CANCELLED items of a FAILED or CANCELLED job, with the original's configuration — the same op, model, prompt and parameters, the same preset at the version the original ran, or the same inline steps and subjects. Only the incomplete items are retried (a variant set may repeat a few that had completed — deterministic work, priced like any other run).
- The new job is an ordinary submit: it is priced, checked and charged like one, so it can be refused like one (
402,422). - A
chainitem restarts on the segment it failed on (failedStep), from the image the last segment it finished made; the segments it already paid for are not paid again. - A source asset deleted since the original ran drops its item from the retry; when none is left the answer is
404 asset_not_found. - The new job points back with
parentJobId, carriesrootJobId— the first attempt's id, the same on every resume and on the first attempt itself — and advancesattemptNumberby 1;GET /api/v1/jobs?rootJobId=lists every attempt. Only the latest attempt can be resumed, and only a few times (details.job.maxResumes).
Whether a resume can help at all is on the job: retryable: true means at least one failed item failed for a reason that may pass.
USE THIS WHEN:
- Retrying only the failed items of a partially-successful job without resubmitting the whole batch
DO NOT USE WHEN:
- Submitting a fresh job → use POST /api/v1/jobs
- Every failed item says
retryable: false— the same input fails the same way
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id (required) | path | string | The id of the job to retry, job_… |
Returns 201 — data is Job
The new attempt, created
Errors
| Status | Code and when |
|---|---|
| 400 |
|
| 402 |
|
| 404 |
|
| 422 |
|
Objects
Each described once; a linked type is another object on this page.
Job
| Field | Type | Description |
|---|---|---|
| actualCredits | integer (int64) | Credits charged: set when the job settles, for the work that completed; 0 until then, and for deterministic work, which spends none |
| attemptNumber | integer (int32) | 1 for a fresh job, one more for each resume |
| cancelledItems | integer (int32) | Items cancelled before they ran |
| collection | string | The collection the outputs go in, as the request named it; absent when it named none (an output made from an asset then lands in that asset's collection) |
| completedAt | string (date-time) | When it reached a terminal status |
| completedItems | integer (int32) | Items that completed |
| createdAt | string (date-time) | When it was submitted |
| errorCode | string | The verdict on the failed items, from the closed set of error codes: a retryable item's code when any is retryable, else the first failed item's. Absent when nothing failed, or no failure was classified |
| errorMessage | string | On a job with failed items, one line for a person ("2 of 3 items failed"); each item carries its own error |
| expiresAt | string (date-time) | When the record is swept — the plan's retention window at submit, the same as the assets it made. Never shortened by a downgrade |
| failedItems | integer (int32) | Items that failed — each says why, and whether a resume could help |
| id | string | The job's id, job_… |
| items | JobItem[] | The items, in order: the first 100 of them. itemsTruncated says when there are more — GET /api/v1/jobs/{id}/items pages the rest |
| itemsTruncated | boolean | True when items is only the first page of them; absent otherwise |
| model | string | The model the job calls; absent for deterministic work, and on a chain, whose segments name their own |
| op | string | The op it was submitted as, when it was submitted as one — what usage groups by |
| parameters | object | What the job runs with: an AI op's model parameters, analyze's schema and maxOutputTokens, or a render's data rows under items |
| parentJobId | string | The attempt this one resumed; absent on a first attempt |
| pipelines | JobPipeline[] | A deterministic job's pipeline as stored at submit — one per variant, named after it — with each layer named by the asset id you gave |
| presetId | string | The preset it ran, by id; absent for an op, inline steps or a render |
| presetName | string | The preset's name; op:<name> for an op, Ad-hoc for a bare type and model; absent for a render |
| presetVersion | integer (int32) | The version of the preset it ran — a later edit of the preset never changes it |
| prompt | string | The prompt the model receives, every {{subject.<name>}} already expanded |
| references | string[] | The reference images sent with every model call — the subjects' images, resolved at submit |
| retryable | boolean | Whether a resume could help: true when at least one failed item failed for a reason that may pass. Absent with errorCode |
| rootJobId | string | The first attempt's id — the job's own on a first attempt, and the same on every resume of it, so GET /api/v1/jobs?rootJobId= lists every attempt |
| segments | JobSegment[] | chain only: the segments each item walks, in order, frozen at submit with what each costs per item |
| settledItems | integer (int32) | Items that reached an end — completed, failed or cancelled |
| status | string | PENDING waits for a slot (an account runs a few jobs of one type at a time; the rest queue), PROCESSING is running and CANCELLING is a cancel waiting for items already running. Then one of the terminal three: COMPLETED (no item failed), FAILED (at least one did — read the items) or CANCELLED.one of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED · CANCELLING |
| stepCount | integer (int32) | How many inline steps the job ran; absent for an op or a preset job |
| steps | PresetStep[] | The inline steps it was submitted with, as written; absent for an op or a preset job |
| subjects | Subject[] | The subjects sent with inline steps, as checked |
| submittedAt | string (date-time) | When it left the queue and started running |
| submittedItems | integer (int32) | Items handed to a provider or worker so far |
| templateId | string | render only: the template, by id |
| templateName | string | render only: the template's name |
| templateVersion | integer (int32) | render only: the version of the template it rendered |
| totalItems | integer (int32) | How many items the job has — one per asset (times the variants), per image to generate, or per render row |
| type | string | What the job runs as — decided by the op, preset or steps it was submitted withone of ai-generate · ai-edit · parse · process · render · chain |
JobCount
How many of your jobs are in one status and of one type. Only non-zero cells are listed.
| Field | Type | Description |
|---|---|---|
| count | integer (int64) | Jobs in this status and of this type, under the request's filters |
| status | string | The cell's statusone of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED · CANCELLING |
| type | string | The cell's job typeone of ai-generate · ai-edit · parse · process · render · chain |
JobEstimate
Cost estimate for a job that has not been created
| Field | Type | Description |
|---|---|---|
| assetCountLeft | integer (int64) | How many more assets the account can hold: the plan's stored-asset ceiling minus the assets it stores now (a chain's intermediates and failed uploads do not count). Deleting assets frees room; below zero after a downgrade. More items than this and a submit is 422 asset_count_exceeded.e.g. 188 |
| costPerItem | integer (int64) | Credits per item (1 credit = $0.0001)e.g. 390 |
| creditBalance | integer (int64) | The account's credit balance right nowe.g. 50000 |
| estimatedCredits | integer (int64) | totalItems × costPerIteme.g. 4680 |
| maxInputEdge | integer (int32) | The longest edge, in pixels, of the image the model will be handed: a larger input is turned upright, scaled down to it and re-encoded as WebP with no metadata first. AI jobs that send an image only; a chain states it per step.e.g. 2048 |
| model | string | Model that would rune.g. "google/gemini-3.1-flash-image-preview" |
| overageCredits | integer (int64) | What overageRuns cost, in credits — already in estimatedCredits. Absent for AI jobs.e.g. 0 |
| overageRuns | integer (int32) | Of this job's deterministic runs, how many fall past the plan's allowance and are paid from your balance instead of refused. 0 when they all fit; absent for AI jobs.e.g. 0 |
| presetId | string | Preset the request resolved to, when it named one |
| presetName | string | Preset name; op:<name> for an op with no preset, 'Ad-hoc' for a bare type + model |
| presetVersion | integer (int32) | The version of that preset the job would run — the one slug@version pinned, or the current onee.g. 3 |
| processCountLeft | integer (int64) | Deterministic-op quota left this period (process / render items), never below zero; -1 when the plan sets no ceiling. Runs your running jobs have claimed are already taken out. Runs past it are paid from your balance — overageRuns · overageCredits. Absent for AI jobs, which are priced in credits.e.g. 190 |
| processPerItem | integer (int32) | chain only: how many process passes each item runs — one per deterministic segment. totalItems × processPerItem is what the job takes out of the period's deterministic-op quota. Every other type runs one pass per item.e.g. 2 |
| prompt | string | The prompt the model would receive, with every {{subject.<name>}} already expanded to that subject's locked descriptor. Check it here rather than discovering a mis-typed placeholder in the pictures. AI jobs only. |
| steps | StepEstimate[] | chain only: what each segment costs, in order, so the total can be added up rather than trusted. Their costPerItem sums to the job's. |
| sufficientCredit | boolean | Whether a submit would be admitted: the balance, plus the one top-up an enabled auto top-up may overdraw, less what your running jobs already hold, covers the estimate. False does NOT mean the request is invalid — it means submitting it now would return insufficient_credit. |
| templateId | string | render only: the template the call named, by ide.g. "builtin-template-og-image" |
| templateName | string | render only: that template's namee.g. "OG image" |
| templateVersion | integer (int32) | render only: the version of the template the job would render — the one id@version pinned, or the current onee.g. 1 |
| totalItems | integer (int32) | Number of items the job would havee.g. 12 |
| type | string | Job type the request resolves toe.g. "ai-generate" |
| warnings | StepWarning[] | What the steps say that is legal, will run, and is probably not what was meant — a shadowed output format, a step the one before it already did. Advisory: a warning never stops a submit, and an empty list is left out. What cannot run at all is a 400 invalid_param here instead, naming the step. Present when the request named a preset or sent inline steps. |
JobItem
One unit of a job — an asset, an image to generate or a render row — and its whole trace: what it made, what it cost, how long it took, and why it failed
| Field | Type | Description |
|---|---|---|
| credits | integer (int64) | What this item cost, in credits; 0 for deterministic work. The items' credits add up to the job's actualCredits |
| currentAssetId | string | chain only: the image its next segment reads — the source, then each segment's product |
| durationMs | integer (int64) | finishedAt − startedAt, in milliseconds |
| error | string | A failed item: why, in one sentence for a person |
| errorCode | string | A failed item: the code to branch on, from the closed set of error codes. Absent when the failure was not classified — treat that as not retryable |
| failedStep | integer (int32) | chain only: the segment a failed item failed on, from 0 — where a resume restarts it |
| finishedAt | string (date-time) | When it settled |
| index | integer (int32) | The item's position in the job, from 0 — how events, the item pages and a resume name it |
| intermediateAssetIds | string[] | chain only: the images earlier segments made to feed the next; deleted when the item settles, except the last one a failed item restarts from |
| model | string | AI items: the model it ran on |
| output | object | analyze only: the answer, in the default schema's shape or your parameters.schema |
| presetVersion | integer (int32) | The preset version it ran — the job's, repeated so one item explains itself |
| provider | string | AI items: the upstream that ran ite.g. "fal" |
| resultAssetId | string | The asset it made, once COMPLETED; absent for analyze, whose product is output |
| retryable | boolean | A failed item: whether a resume could succeed — the code's own flag |
| sourceAssetId | string | The asset it runs on; absent for an image generated from a prompt or a render row |
| startedAt | string (date-time) | When a worker picked it up |
| status | string | Where the item isone of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED |
| step | integer (int32) | chain only: the segment the item is on, from 0 |
| templateVersion | integer (int32) | A render item's template version — the job's, repeated |
| timings | object | AI items: where the time went, in milliseconds — inputMs (making the image the model is handed), providerMs (the provider call), storeMs (fetching and storing the result). Exclusive of one another, summed over retries |
| variant | string | The variant that made it; absent outside a variant set |
JobPipeline
One pass a deterministic job runs: a name — the variant's, when there is a set of them — and its registry steps
| Field | Type | Description |
|---|---|---|
| name | string | The variant's name, or op:<name> for a single op |
| steps | PipelineStep[] | The registry steps, in order |
JobRequest
What to run and on what: one of op, presetId or steps (or a raw type), the inputs, and where the outputs go. The same body prices it with ?dryRun=true.
| Field | Type | Description |
|---|---|---|
| assetIds | string[] | 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. |
| collection | string | 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.e.g. "shoot-01" |
| count | integer (int32) | A job with no assetIds that makes images from a prompt: how many to make, one item each. 1 when absent.e.g. 1 |
| imageCount | integer (int32) | 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.e.g. 20 |
| items | object[] | render_template: one object of template variables per output image (≤ 500). May also be given as parameters.items.e.g. [{"site":"imagestep.dev","title":"Hello"}] |
| model | string | 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. |
| op | string | 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.e.g. "remove_bg" |
| parameters | object | 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. |
| preset | PipelineData | A raw processing pipeline — registry steps, run as one pass. What an op translates into; prefer op, or steps for several. |
| presetId | string | 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.e.g. "builtin-util-web-optimize" |
| prompt | string | 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. |
| retentionDays | integer (int32) | 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.e.g. 7 |
| steps | PresetStep[] | An inline chain: the same steps a preset stores (each {op, model?, prompt?, parameters?} or {operation, params}), checked as a preset's are and run once without saving one. Sent instead of op or presetId; model, prompt and parameters go on the step. The job records them. |
| subjects | Subject[] | With steps only: the subjects a preset stores — reference images plus the locked words, applied to every generate / edit step, with {{subject.<name>}} in a prompt expanding to the descriptor. Checked as a preset's are (your own DONE assets, at most 4 subjects, and a generate or edit step to read them). The job records them. |
| templateId | string | render_template: the template to render — an id, or id@version to pin one version. May also be given as parameters.templateId.e.g. "builtin-template-og-image" |
| type | string | 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.one of ai-generate · ai-edit · parse · process · render · chain |
| variants | object[] | 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. |
| wait | integer (int32) | 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.e.g. 30 |
JobSegment
One segment of a chain: a deterministic pass or one AI step, and what it costs per item
| Field | Type | Description |
|---|---|---|
| bound | boolean | True when the model bills by the size of the image it is sent: costPerItem was priced at the largest image it is ever sent, and it is the charge whatever the size of an item's own |
| costPerItem | integer (int64) | Credits one item costs on this segment, frozen at submit; 0 for a deterministic one |
| model | string | An AI segment's model |
| op | string | The op the segment runs; absent for a pass built from registry steps only |
| parameters | object | An AI segment's parameters |
| pipeline | PipelineStep[] | A deterministic segment's steps, as one pass |
| prompt | string | An AI segment's prompt, every {{subject.<name>}} expanded |
| type | string | What the segment runs as: a deterministic pass, or an AI op's job typeone of ai-generate · ai-edit · parse · process |
JobSummary
One row of GET /api/v1/jobs: the job's own fields, as on Job — what ran, how far it got, what it cost — without items, steps, subjects, pipelines, segments, parameters, prompt, references or collection. Read the job by id for those.
| Field | Type | Description |
|---|---|---|
| actualCredits | integer (int64) | |
| apiKeyId | string | The API key that submitted it; absent for a signed-in console session |
| attemptNumber | integer (int32) | |
| cancelledItems | integer (int32) | |
| completedAt | string (date-time) | |
| completedItems | integer (int32) | |
| costPerItem | integer (int64) | Credits one item is priced at — what the job was quoted; 0 for deterministic work, and on a chain the sum of its segments |
| createdAt | string (date-time) | |
| errorCode | string | |
| errorMessage | string | |
| expiresAt | string (date-time) | |
| failedItems | integer (int32) | |
| id | string | |
| model | string | |
| op | string | |
| parentJobId | string | |
| presetId | string | |
| presetName | string | |
| presetVersion | integer (int32) | |
| retryable | boolean | |
| rootJobId | string | |
| settledItems | integer (int32) | |
| status | string | one of PENDING · PROCESSING · COMPLETED · FAILED · CANCELLED · CANCELLING |
| stepCount | integer (int32) | |
| submittedAt | string (date-time) | |
| submittedItems | integer (int32) | |
| templateId | string | |
| templateName | string | |
| templateVersion | integer (int32) | |
| totalItems | integer (int32) | |
| type | string | one of ai-generate · ai-edit · parse · process · render · chain |
PipelineData
An inline processing pipeline: a name and the registry steps a process job runs.
| Field | Type | Description |
|---|---|---|
| name | string | |
| pipeline | PipelineStep[] |
PipelineStep
| Field | Type | Description |
|---|---|---|
| disabled | boolean | |
| operation | string | |
| params | any |
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. |
StepEstimate
One segment of a chain job's cost
| Field | Type | Description |
|---|---|---|
| bound | boolean | True when this segment's model is priced by the size of the image it is sent: costPerItem is set at the largest image that model is ever sent (maxInputEdge), and it is what every item is charged, whatever the size of its own image. Absent means the price does not depend on the image. |
| costPerItem | integer (int64) | Credits one item costs on this segment; 0 for a process segmente.g. 390 |
| index | integer (int32) | Segment index, 0-basede.g. 0 |
| maxInputEdge | integer (int32) | The longest edge, in pixels, of the image this segment's model is handed; absent for a process segmente.g. 2048 |
| model | string | The model this segment would call; absent for a process segment |
| op | string | What the segment runs: process for a pipeline pass, else the AI op's namee.g. "remove_bg" |
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. |