Skip to content

n8n node

One community node and one trigger. Hand it the binary an earlier node produced — from Google Drive, an HTTP Request, a form — and get the transformed image straight back, or keep the result as an asset with a stable public URL; or let the trigger start a workflow when a job finishes, from a signed webhook, so nothing polls. Zero runtime dependencies: every request goes through n8n's own HTTP helpers.

Install and credentials

In n8n: Settings → Community Nodes → Install, enter n8n-nodes-imagestep, accept the prompt. A self-hosted n8n can also take it from a shell — in its nodes directory (inside the container, for Docker):

cd ~/.n8n/nodes && npm install n8n-nodes-imagestep

Restart n8n and ImageStep and ImageStep Trigger appear in the node picker. Then create an ImageStep API credential: the API Key is a key from /keys, and Base URL stays https://api.imagestep.dev (change it only for a self-hosted ImageStep). The credential's own test reads the key's usage, which only a valid key can: a green test proves both that the base URL reaches ImageStep and that the key is right, and a wrong or revoked key tests red.

The ImageStep node

One node, a Resource and an Operation dropdown. It is marked usable as an AI-agent tool, so an n8n agent can call it too.

Asset

operationwhat it doesfields
UploadUpload a binary file from the input itemtakes the item's binary property, stages, PUTs the bytes, finishes — and with Wait Until Ready on, waits for dimensions and metadata. Output: an asset reference (assetId, name, status, mimeType, width, height, size, collection, publicUrl, expiresAt)Binary Property (default data) · Collection · Retention Days (default 0) · Wait Until Ready (default on)
Upload From URLHave ImageStep fetch public image URLs and create an asset from each — nothing is downloaded into n8nany number of URLs, sent 20 to a request and waited for together. One output item per URL — the reference plus url, or { url, error } for a link that is private, dead or not an image, so you can route those instead of stoppingImage URLs · Collection · Retention Days (default 0) · Wait Until Ready (default on)
GetFetch one asset by ID (dimensions, metadata, public URL)Asset ID
ListSearch assets by collection, keyword, mime type, ingest state or when they were madeeach item carries page, total, hasMore and nextCursor — page from 0, Per Page 100 at most; Cursor reads the page after a nextCursorCollection · Search · MIME Type · Status (Any · Done · Failed · Processing) · Created After · Created Before · Page (default 0) · Cursor · Per Page (default 100)
List CollectionsYour collections with how many assets each holds, most recently added to firstone item per collection — collection, count, lastAddedAtName Contains · Page (default 0) · Cursor · Per Page (default 100)
PublishPublish assets so each gets a stable public URL on the CDNcomma-separated ids; each publicUrl serves the asset itself at full size, never a thumbnailAsset IDs

Operation

Run — one atomic op, synchronously or as a job. The Op dropdown is loaded live from the catalogue and falls back to a built-in list when that call fails. A field shows only when the ones before it make it apply — Wait Seconds only while Wait for Result is on, and so on.

fielddefaultmeaning
Opdefault remove_bgloaded live from the catalogue, with a built-in list when that call fails — /docs/ops is the list
Inputdefault binary; Binary File · Asset IDs · NoneBinary File (the item's binary property), Asset IDs (comma-separated; one job over all of them), or None (for generate)
Binary Propertydefault dataName of the binary property holding the image
Asset IDsOne asset ID, or several separated by commas — one job over all of them
PromptWhat to draw (generate) or the edit instruction (edit)
Countdefault 1Images to generate
Parametersdefault {}JSON, e.g. {"width": 1200, "fit": "inside"} for resize or {"scaleFactor": 2} for upscale; a wrong value is a 400 invalid_param naming the parameter, before any credit is spent
Dry Rundefault offprice only: { dryRun: true, request, estimate } with estimatedCredits, sufficientCredit, processCountLeft and assetCountLeft; nothing is created or uploaded. A Binary File is priced as one image (imageCount: 1 in request), since the price never depends on the pixels
Store Resultdefault offoff: one image in — a Binary File or one Asset ID — and the transformed bytes straight back out on the binary property it came in on (data for an Asset ID), with { op, mode: "sync", stored: false, bytes, mimeType } as the item; nothing created, nothing published, nothing polled. On: it runs as a job, so you get an asset id, a permanent URL, progress and webhooks. Which ops may take the off path is the API's answer (syncEndpoint): AI ops always run as a job, and so does anything that is not one image. A job that runs with this off is waited for (up to 180 s) and publishes nothing — its outputs come back as asset ids with no publicUrl; turn it on to set Wait for Result, Wait Seconds, Publish Outputs, Download Outputs
Wait for Resultdefault onoff returns the job handle at once — continue with Job → Wait or the trigger
Wait Secondsdefault 180How long to wait before giving up (the job keeps running)
Publish Outputsdefault onWhether to publish result assets so each output has a stable publicUrl on the CDN: the output itself at full size, not a thumbnail
Download Outputsdefault offfetches each output into the binary property data, one item per result
OptionsModel · Collection · Retention DaysModel overrides the op's default; Collection is where the outputs go (default: the input's)

Output: a job reference — jobId, type, status, totalItems, completedItems, failedItems, creditsCharged, items[] (the first 100; itemsTruncated past that) and, once finished, outputs[] — every output, with publicUrl. The fields are named as the MCP server names them, so an agent and a workflow read the same keys. Each item carries its own trace — status, resultAssetId, errorCode, retryable, provider, model, credits — and, for a chain, step and failedStep: which segment of the preset it is on, and which one it failed on. With Dry Run the output is { dryRun, request, estimate } instead, and a chain's estimate carries steps[] — the price segment by segment.

Preset and Job

operationwhat it doesfields
Preset → RunRun a saved preset over assets as a joba saved preset over assets: built-ins first, then yours; Version 0 runs the current one; every result is a new asset. A preset whose steps mix a model with other steps runs as one chain job. A preset always runs as a job, so Store Result decides only what becomes of its outputs: off, the job is waited for (up to 180 s) and nothing is published — the outputs come back as asset ids; on shows Wait for Result, Wait Seconds, Publish Outputs, Download OutputsPreset · Version (default 0) · Input (default binary; Binary File · Asset IDs · None) · Binary Property (default data) · Asset IDs · Prompt · Count (default 0) · Dry Run (default off) · Store Result (default off) · Wait for Result (default on) · Wait Seconds (default 180) · Publish Outputs (default on) · Download Outputs (default off) · Options (Collection · Retention Days)
Job → GetRead a job's status and, once finished, its outputsa finished job's outputs are collected and, with Publish Outputs, publishedJob ID · Publish Outputs (default on) · Download Outputs (default off)
Job → WaitPoll a job until it finishes and return its outputsthe service holds each read open, so nothing polls; then as GetJob ID · Wait Seconds (default 180) · Publish Outputs (default on) · Download Outputs (default off)

The ImageStep Trigger

A webhook trigger. On activation it registers an endpoint on your account pointing at the workflow's webhook URL, subscribed to the Events you pick — job.completed and job.failed by default, job.item.completed and job.item.failed opt-in — and deletes it on deactivation. Every delivery is verified before the workflow runs: the ImageStep-Signature HMAC over the raw body, a timestamp inside Signature Tolerance (300 s by default), a constant-time compare. A delivery that fails is answered 401 and never reaches the workflow; a good one is answered 200 at once and emits { id, type, createdAt, attempt, data }. Deduplicate on id if you must be exactly-once — the API retries a delivery that is not acknowledged, for up to 24 hours; what each event's data holds is on webhooks.

  • ImageStep registers only https:// URLs on public hosts. A local n8n on http://localhost:5678 cannot activate the trigger — set n8n's WEBHOOK_URL to a public https address, a tunnel for instance.
  • The signature is over the raw request bytes. If a reverse proxy strips rawBody, the node re-serialises the parsed JSON, which verifies only when byte-identical; 401s in the endpoint's delivery log are the sign.

Templates

Two importable workflows ship in the npm package, under templates/ — on a self-hosted n8n installed as above, in ~/.n8n/nodes/node_modules/n8n-nodes-imagestep/templates/ — and sit next to the node in the repository, in packages/n8n-nodes-imagestep/templates/. Import one with Workflows → Import from file, then pick your own credentials, folder and sheet in each node:

  1. Google Drive folder → remove_bg → Google Sheets: a new file in a folder, background removed, a row with the public URL.
  2. Google Sheets prompt → generate → publish: the URL written back to the same row. To keep the product identical across rows, swap the generate node for Preset → Run on a consistency preset, with Input set to None and the row’s scene as its Prompt — the preset’s subjects stay fixed and each row brings its own scene.

The longer pipelines on recipes — a cut-out, a packshot and a web size from one upload, the same character on every card, the same product in every scene, a template per row — are n8n templates too, each beside a Node script that runs the same pipeline, in the open-source recipes repo.

Errors, cost and idempotency

  • An API error becomes an n8n error whose message reads ImageStep <code> (param: <name>): <message> and whose description carries retryable. With Continue On Fail the item becomes { error, code, param, retryable } instead of stopping the run; with Retry On Fail, branch on retryable. The codes are the closed set on errors, retries & limits.
  • A job that finishes with failed items is not an error: failedItems and each item's error say what happened, and the successful outputs are still returned.
  • AI ops charge credits per item; deterministic ops are free on paid plans and count against the Free plan's monthly quota (pricing). Dry Run shows the exact price.
  • When Wait Seconds runs out the item fails with Job … still PROCESSING and retryable=true: the job keeps running and is charged for what completes. Retry On Fail picks the same job back up (same key, below); with Continue On Fail the item is the job reference itself, with timedOut: true beside error — branch on it and continue with Job → Wait on its jobId. For a long batch turn Wait for Result off and continue with Job → Wait or the trigger.
  • Every write carries an idempotency key made from the execution, the node, the item and the request, and a retryable answer is tried twice more with that same key, waiting what Retry-After says. So Retry On Fail, which runs the node again over every input item, gets back the jobs an earlier try already submitted instead of creating and charging them again. A refusal is not retried, and its description carries the details — insufficient_credit's topUpUrl among them, for whoever reads the failed run. Running the workflow again is a new execution, and does the work again.

Tested against n8n's n8n-workflow 2.x API; Node 18+. Source and changelog in the repository.