---
title: n8n node
url: https://base_url.placeholder/docs/n8n
group: Surfaces
---

# 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):

```sh
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](https://base_url.placeholder/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

| operation | what it does | fields |
| --- | --- | --- |
| Upload | Upload a binary file from the input item<br>takes 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 URL | Have ImageStep fetch public image URLs and create an asset from each — nothing is downloaded into n8n<br>any 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 stopping | Image URLs · Collection · Retention Days (default 0) · Wait Until Ready (default on) |
| Get | Fetch one asset by ID (dimensions, metadata, public URL) | Asset ID |
| List | Search assets by collection, keyword, mime type, ingest state or when they were made<br>each item carries page, total, hasMore and nextCursor — page from 0, Per Page 100 at most; Cursor reads the page after a nextCursor | Collection · Search · MIME Type · Status (Any · Done · Failed · Processing) · Created After · Created Before · Page (default 0) · Cursor · Per Page (default 100) |
| List Collections | Your collections with how many assets each holds, most recently added to first<br>one item per collection — collection, count, lastAddedAt | Name Contains · Page (default 0) · Cursor · Per Page (default 100) |
| Publish | Publish assets so each gets a stable public URL on the CDN<br>comma-separated ids; each publicUrl serves the asset itself at full size, never a thumbnail | Asset IDs |

### Operation

**Run** — one atomic op, synchronously or as a job. The **Op** dropdown is loaded live from the [catalogue](https://base_url.placeholder/docs/ops) 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.

| field | default | meaning |
| --- | --- | --- |
| Op | default remove_bg | loaded live from the catalogue, with a built-in list when that call fails — /docs/ops is the list |
| Input | default binary; Binary File · Asset IDs · None | Binary File (the item's binary property), Asset IDs (comma-separated; one job over all of them), or None (for generate) |
| Binary Property | default data | Name of the binary property holding the image |
| Asset IDs |   | One asset ID, or several separated by commas — one job over all of them |
| Prompt |   | What to draw (generate) or the edit instruction (edit) |
| Count | default 1 | Images to generate |
| Parameters | default {} | 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 Run | default off | price 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 Result | default off | off: 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 Result | default on | off returns the job handle at once — continue with Job → Wait or the trigger |
| Wait Seconds | default 180 | How long to wait before giving up (the job keeps running) |
| Publish Outputs | default on | Whether to publish result assets so each output has a stable publicUrl on the CDN: the output itself at full size, not a thumbnail |
| Download Outputs | default off | fetches each output into the binary property data, one item per result |
| Options | Model · Collection · Retention Days | Model 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](https://base_url.placeholder/docs/mcp) 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

| operation | what it does | fields |
| --- | --- | --- |
| Preset → Run | Run a saved preset over assets as a job<br>a 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 Outputs | Preset · 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 → Get | Read a job's status and, once finished, its outputs<br>a finished job's outputs are collected and, with Publish Outputs, published | Job ID · Publish Outputs (default on) · Download Outputs (default off) |
| Job → Wait | Poll a job until it finishes and return its outputs<br>the service holds each read open, so nothing polls; then as Get | Job 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](https://base_url.placeholder/docs/webhooks#retries) a delivery that is not acknowledged, for up to 24 hours; what each event's `data` holds is on [webhooks](https://base_url.placeholder/docs/webhooks#events).

- 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; `401`s 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/`](https://github.com/imagestep/imagestep-sdk/tree/main/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](https://base_url.placeholder/docs/recipes#pipelines) — 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](https://github.com/imagestep/imagestep-sdk/tree/main/recipes).

## 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](https://base_url.placeholder/docs/errors).
- 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](https://base_url.placeholder/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](https://github.com/imagestep/imagestep-sdk/tree/main/packages/n8n-nodes-imagestep).
