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
Request
- Base URL: https://api.imagestep.dev
- URL: https://api.imagestep.dev/api/v1/jobs
- Auth: API key in header Authorization
Query parameters
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.
dryRun spelled snake_case; either one set to true is a dry run.
Headers
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
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.