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.

1 — list your workflows and their inputs
curl https://pincel.app/api/v1/workflows \
  -H "X-API-Key: $PINCEL_API_KEY"
2 — run it with your image
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"}}'
3 — poll until it finishes
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.

Header
X-API-Key: 3f7a1c92-8e54-4b0d-9a6f-2c1e5d840b7a

Keep the key server-side

A key can spend your credits. Never ship it in a browser, a mobile app, or a public repo — there's deliberately no CORS on this API to discourage it.

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:

GET /api/v1/workflows/:id
{
  "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" }
  ]
}
FieldMeaning
nodeIdThe key to use in your inputs object.
typeimage expects a public image URL; text expects a string.
cardinalitysingle takes one value; list takes an array and runs a batch.
hasStoredValueWhether the saved workflow already has a value here. If true, you can omit it.

Omitted inputs keep their saved values

A pipeline with a stored brand reference or a fixed prompt only needs the one field that changes per call.

Image URLs must be publicly reachable over http(s). Private and loopback addresses are rejected.

Endpoints#

Method & pathPurpose
GET /api/v1/workflowsList your workflows with inputs, steps and cost.
GET /api/v1/workflows/:idDescribe one workflow.
POST /api/v1/workflows/:id/runStart a run, or a batch.
GET /api/v1/runs/:runIdPoll a run.
GET /api/v1/batches/:batchIdPoll a batch.

List workflows#

Everything an agent needs to pick a workflow and fill it in.

GET /api/v1/workflows
{
  "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.

Request body
{
  "inputs": {
    "upload-1": "https://example.com/product.jpg",
    "text-1": "on a marble surface, soft daylight"
  }
}
Response
{
  "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. Video-producing steps (Image to Video) return .mp4 URLs with the same retention rules.

GET /api/v1/runs/:runId
{
  "run_id": "b81e…",
  "workflow_id": "7c4f…",
  "status": "succeeded",
  "error": null,
  "credits_used": 7,
  "outputs": ["https://static.pincel.app/workflows/…/result.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.

GET /api/v1/batches/:batchId
{
  "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/…"]
    },
    {
      "run_id": "…",
      "index": 1,
      "label": "sku-2.jpg",
      "status": "running",
      "outputs": []
    }
  ]
}

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.

POST /api/v1/workflows/:id/run
{
  "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.

Connect a client
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-Key or Authorization: Bearer for clients that only support bearer tokens.
  • Transport — stateless Streamable HTTP: one JSON-RPC message per POST, no sessions, no SSE stream.
  • Waiting — a run tool waits up to ~50 seconds and returns the outputs directly; longer runs come back as status: "running" with a run_id to poll via get_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

Tool schemas mark only inputs without a stored value as required — a workflow with a saved reference image just runs, the same as over REST.

Polling & webhooks#

Poll every ~2 seconds. There are no delivery webhooks yet.

Polling also drives the work

A poll doesn't just read status — it nudges a stalled run forward if a provider callback was missed. A client that stops polling leaves the run to a background sweeper (within ~5 minutes) instead of finishing promptly.

Run outputs expire after 7 days

Results are temporary for every account — download them, or add a Save to Library node to the workflow to keep specific results. Saved assets appear in your Media Library and are returned as 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-remaining

When 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.

402 — out of credits
{
  "error": "Out of credits. Top up at https://pincel.app/billing to keep running workflows.",
  "code": 402,
  "credits_remaining": 0
}

Rotating your key

You can issue a new key any time from your account page. The old one stops working immediately, so update your integrations first.

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.

BucketLimit
POST …/run60 / minute
Polling (/runs, /batches)600 / minute
Discovery (/workflows)120 / minute
Runs in flight at once75
  • 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 429 carries Retry-After and X-RateLimit-* headers. Back off rather than retrying immediately — the window is a rolling 60 seconds.

Need more throughput?

These aren't hard product limits. Ask us and we'll raise them for your account.

Errors#

CodeMeaning
400Bad 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.
401Missing, unknown or rotated API key.
402Out of credits — the balance is zero. Includes credits_remaining so an agent can act on it.
404Workflow, run or batch not found — or not yours.
405Wrong method.
429Rate 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 and every saved workflow appears as a callable 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.

© 2026 PincelBrand-safe by design · content policy enforced