API reference · v1
Workflows API
Build an image pipeline once on the Pincel canvas, then run it from your code — or let an AI agent run it for you. Same models, same credits, no UI required.
On this page
Overview#
A workflow is a graph of Pincel tools you assemble on the canvas: remove a background, drop the product into a generated scene, relight it, upscale it, export. Once it's saved, this API runs that same graph with whatever inputs you pass in.
https://pincel.app— base URL. All responses are JSON.- Runs are asynchronous: start a run, then poll it.
- Credits are charged per step, exactly as in the app. The same content-safety checks apply to every step.
- Outputs are temporary — deleted after 7 days. Add a Save to Library node to keep a result permanently.
- Pass an array to a List input and the workflow runs once per row — a whole product catalog in one call.
- Using an AI assistant? Connect it to the MCP server and every saved workflow shows up as a tool it can call.
Quickstart#
Three calls: find your workflow, run it, poll for the result.
curl https://pincel.app/api/v1/workflows \
-H "X-API-Key: $PINCEL_API_KEY"curl -X POST https://pincel.app/api/v1/workflows/$WORKFLOW_ID/run \
-H "X-API-Key: $PINCEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs":{"upload-1":"https://example.com/product.jpg"}}'curl https://pincel.app/api/v1/runs/$RUN_ID \
-H "X-API-Key: $PINCEL_API_KEY"Authentication#
Send your key in the X-API-Key header. It's a UUID — copy it from your account page.
X-API-Key: 3f7a1c92-8e54-4b0d-9a6f-2c1e5d840b7aKeep the key server-side
A 401 means the key is unknown or was rotated; a zero balance answers 402 instead — see Credit balance. Credits — not a subscription — are what entitle you here.
Inputs & node ids#
A workflow's source nodes are its inputs, and you address them by node id. Ask the API what they are rather than guessing:
{
"id": "7c4f…",
"name": "Product shot cleanup",
"estimated_credits_per_run": 7,
"inputs": [
{
"nodeId": "upload-1",
"type": "image",
"cardinality": "single",
"required": true,
"hasStoredValue": false
}
],
"steps": [
{ "nodeId": "remove-bg-1", "tool": "remove-bg", "name": "Remove Background" },
{ "nodeId": "upscale-1", "tool": "upscale", "name": "Upscale" }
]
}| Field | Meaning |
|---|---|
nodeId | The key to use in your inputs object. |
type | image expects a public image URL; text expects a string. |
cardinality | single takes one value; list takes an array and runs a batch. |
hasStoredValue | Whether the saved workflow already has a value here. If true, you can omit it. |
Omitted inputs keep their saved values
Image URLs must be publicly reachable over http(s). Private and loopback addresses are rejected.
Endpoints#
| Method & path | Purpose |
|---|---|
GET /api/v1/workflows | List your workflows with inputs, steps and cost. |
GET /api/v1/workflows/:id | Describe one workflow. |
POST /api/v1/workflows/:id/run | Start a run, or a batch. |
GET /api/v1/runs/:runId | Poll a run. |
GET /api/v1/batches/:batchId | Poll a batch. |
List workflows#
Everything an agent needs to pick a workflow and fill it in.
{
"workflows": [
{
"id": "7c4f…",
"name": "Product shot cleanup",
"updated_at": "2026-07-25T10:02:11.000Z",
"estimated_credits_per_run": 7,
"inputs": [ /* … */ ],
"steps": [ /* … */ ]
}
]
}Describe a workflow#
The same shape for a single workflow — see Inputs & node ids.
Run a workflow#
POST /api/v1/workflows/:id/run with an inputs object. Returns immediately with a run id.
{
"inputs": {
"upload-1": "https://example.com/product.jpg",
"text-1": "on a marble surface, soft daylight"
}
}{
"run_id": "b81e…",
"status": "running",
"poll": "/api/v1/runs/b81e…"
}Poll a run#
status is running, succeeded, failed or canceled. outputs holds the end results — the steps nothing else consumes — which is usually what you want; steps exposes intermediates too. When the workflow has an Export node, exports holds its files with the node's background, size and format applied (empty otherwise). Video-producing steps (Image to Video) return .mp4 URLs with the same retention rules.
{
"run_id": "b81e…",
"workflow_id": "7c4f…",
"status": "succeeded",
"error": null,
"credits_used": 7,
"outputs": ["https://static.pincel.app/workflows/…/result.png"],
"exports": ["https://static.pincel.app/workflows/…/export.jpg"],
"steps": [
{
"node_id": "upscale-1",
"tool": "upscale",
"status": "succeeded",
"outputs": ["https://static.pincel.app/…"],
"error": null,
"credits_used": 4
}
]
}Poll a batch#
One entry per List row, each with its own status and results.
{
"batch_id": "3f0a…",
"status": "running",
"done": false,
"credits_used": 7,
"rows": [
{
"run_id": "…",
"index": 0,
"label": "sku-1.jpg",
"status": "succeeded",
"error": null,
"credits_used": 7,
"outputs": ["https://static.pincel.app/…"],
"exports": ["https://static.pincel.app/…"]
},
{
"run_id": "…",
"index": 1,
"label": "sku-2.jpg",
"status": "running",
"outputs": [],
"exports": []
}
]
}Batch a catalog#
Add a List node to the workflow on the canvas, then pass an array to it. The whole pipeline runs once per row, rows are independent — one failure doesn't stop the others — and you poll a single batch id.
{
"inputs": {
"list-1": [
"https://example.com/sku-1.jpg",
"https://example.com/sku-2.jpg",
"https://example.com/sku-3.jpg"
]
}
}- Up to 25 rows per call.
- A text List (one prompt per row) works the same way — useful for generating variants of one asset.
- The whole batch is checked against your balance up front, so it won't die halfway with some rows already charged.
MCP server#
https://pincel.app/api/v1/mcp is this API reshaped for agents: every saved workflow is one MCP tool, with an input schema generated from its graph — one property per input node id, arrays for List inputs. Two extra tools, get_run_status and get_batch_status, cover the asynchronous part. Connect an assistant once and it can discover and run your pipelines without composing REST calls.
Four direct tools work before you have saved anything — they take public image URLs and cost the same as the matching canvas node:
generate_image— prompt, model, aspect ratio, up to 4 images and optional reference images.edit_image— change an image by instruction (Nano Banana 2, GPT Image or FLUX 2 Pro).remove_background— transparent cutout, 1 credit.make_ad— a product photo turned into an ad image with an optional headline and call-to-action, ~9 credits.
claude mcp add --transport http pincel https://pincel.app/api/v1/mcp \
--header "X-API-Key: $PINCEL_API_KEY"- Auth — the same API key, sent as
X-API-KeyorAuthorization: Bearerfor clients that only support bearer tokens. - Transport — stateless Streamable HTTP: one JSON-RPC message per POST, no sessions, no SSE stream.
- Waiting — a run or direct tool waits up to ~50 seconds and returns the outputs directly; longer runs come back as
status: "running"with arun_idto poll viaget_run_status. - Credits & limits — every tool result includes
credits_remaining; the rate limits are the same buckets as REST (list → discovery, run → start, status → polling).
Inputs with saved values are optional
Polling & webhooks#
Poll every ~2 seconds. There are no delivery webhooks yet.
Polling also drives the work
Run outputs expire after 7 days
saved_outputs alongside the temporary outputs.Credit balance#
Every response tells you where your balance stands, so an unattended integration can warn its owner before runs start failing:
- Header
X-Credits-Remaining: 4821— on every request, including errors. - Field
credits_remaining— in every JSON body.
curl -si https://pincel.app/api/v1/runs/$RUN_ID \
-H "X-API-Key: $PINCEL_API_KEY" | grep -i x-credits-remainingWhen the balance reaches zero, requests answer 402 rather than 401, so you can handle it specifically — a 401 then means only that the key is wrong or was rotated.
{
"error": "Out of credits. Top up at https://pincel.app/billing to keep running workflows.",
"code": 402,
"credits_remaining": 0
}Rotating your key
Rate limits#
Credits are the real throughput limit — every run spends them. These limits exist to protect shared model capacity, to stop a retry loop from burning credits by accident, and to block key guessing. The same limits apply to every account.
| Bucket | Limit |
|---|---|
POST …/run | 60 / minute |
Polling (/runs, /batches) | 600 / minute |
Discovery (/workflows) | 120 / minute |
| Runs in flight at once | 75 |
- 60 starts/minute is ≥1,500 rows/minute when batched, so a real catalog job never touches it.
- Runs in flight is the limit that matters most: one request can start 25 runs, so it's checked before a batch begins.
- A
429carriesRetry-AfterandX-RateLimit-*headers. Back off rather than retrying immediately — the window is a rolling 60 seconds.
Need more throughput?
Errors#
| Code | Meaning |
|---|---|
400 | Bad input — unknown node id, wrong value shape, a private URL, over the 25-row batch limit, or a required input with no value. The response repeats inputs so you can see what is missing. |
401 | Missing, unknown or rotated API key. |
402 | Out of credits — the balance is zero. Includes credits_remaining so an agent can act on it. |
404 | Workflow, run or batch not found — or not yours. |
405 | Wrong method. |
429 | Rate limit or in-flight run cap reached. |
A run that can't be afforded in full is rejected up front with a message naming the shortfall, rather than dying halfway with some steps already charged. Content that fails moderation isn't an HTTP error: the step ends with status blocked and isn't charged.
FAQ#
Do I need a subscription to use the Workflows API?
No. Credits are what entitle you — you can buy a credit pack without subscribing and use the API the same way. Requests are rejected only when your balance reaches zero.
How much does a run cost?
Exactly what the same steps cost in the app: the sum of each step in the workflow. Call GET /api/v1/workflows to see estimated_credits_per_run before you run anything. Failed and content-blocked steps are not charged.
Can I run one workflow over many images at once?
Yes. Put a List node in the workflow and pass an array of image URLs to it. The workflow runs once per row, up to 25 rows per call, and you poll a single batch id for all of them.
How long do result URLs last?
Run outputs are temporary — they are deleted after 7 days, for every account. Download what you need, or add a Save to Library node to the workflow: whatever feeds that node is copied to permanent storage and appears in your Pincel Media Library.
How do I know when my credits are running low?
Every response includes your balance — an X-Credits-Remaining header and a credits_remaining field — so an agent or script can warn you before runs start failing. When the balance reaches zero, requests answer 402 instead of 401 so you can react to that case specifically.
Can I regenerate my API key?
Yes — there is a Regenerate key button on your account page. The old key stops working immediately, so update your integrations before rotating.
Can my AI assistant run workflows directly?
Yes — connect it to the MCP server at https://pincel.app/api/v1/mcp with your API key. Four tools work straight away — generate_image, edit_image, remove_background and make_ad — and every saved workflow appears as its own tool, plus status tools for polling. Works with Claude Code, Claude Desktop and any MCP client that supports HTTP servers with custom headers.
Can I call this from a browser?
No — this is a server-to-server API. There is no CORS, because calling it from a browser would expose your API key to anyone who views the page.
Build your first workflow
Start from a template on the canvas, then call it from your code with the three requests above.