# Runflow docs (full) Generated from https://docs.runflow.io. Public OpenAPI: https://docs.runflow.io/api/openapi.public.json. Model catalog: https://www.runflow.io/models-catalog.json. --- # API catalog [RFC 9727](https://datatracker.ietf.org/doc/rfc9727/) defines an "API catalog" linkset: one URL that lists every machine-readable surface of an API. Runflow's lives at: ``` https://www.runflow.io/.well-known/api-catalog ``` ## Payload ```json { "linkset": [ { "anchor": "https://api.runflow.io/", "service-desc": [ { "href": "https://docs.runflow.io/api/openapi.public.json", "type": "application/json" } ], "service-doc": [ { "href": "https://docs.runflow.io/api-reference/models/search-models", "type": "text/html" }, { "href": "https://docs.runflow.io/", "type": "text/html" } ], "status": [ { "href": "https://api.runflow.io/v1/health", "type": "application/json" } ] } ] } ``` ## Relations | Rel | What it points to | |---|---| | `service-desc` | Machine-readable public spec (OpenAPI). | | `service-doc` | Human-readable API reference and docs. | | `status` | Health/status endpoint. Both [`/v1/health`](https://api.runflow.io/v1/health) (thin) and [`/v1/readiness`](https://api.runflow.io/v1/readiness) (per-component checks) are reachable. | ## Use it Discovery agents use the catalog to learn the spec URL without scraping. Code: ```python import requests from requests.exceptions import RequestException TIMEOUT = 5 try: r = requests.get("https://www.runflow.io/.well-known/api-catalog", timeout=TIMEOUT) r.raise_for_status() catalog = r.json() spec_url = catalog["linkset"][0]["service-desc"][0]["href"] r = requests.get(spec_url, timeout=TIMEOUT) r.raise_for_status() spec = r.json() except RequestException as exc: raise SystemExit(f"Could not load Runflow API catalog: {exc}") ``` ## Related The spec the catalog points at. Higher-level agent integration. --- # For agents For agent integrations, start with the **Runflow skill**. It is the shortest path to the correct workflow. ## Install the skill If your coding agent has file access, it can install the skill for you. Open the agent in the repo where you are integrating Runflow and ask: ```text Install the Runflow Agent Skill for this project. Create .agents/skills/runflow/SKILL.md by downloading: https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md Then use that skill whenever this repo integrates, calls, or debugs Runflow. Use OpenAPI for endpoint schemas and auth, and the models catalog for model availability and pricing. Never write API keys to files. ``` If you prefer to install it yourself, run this from the project root: ```bash mkdir -p .agents/skills/runflow curl -fsSL https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md \ -o .agents/skills/runflow/SKILL.md ``` For a user-wide install, use your home directory instead: ```bash mkdir -p ~/.agents/skills/runflow curl -fsSL https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md \ -o ~/.agents/skills/runflow/SKILL.md ``` Agents with native skill managers may also have their own install command or directory. Common project directories are `.agents/skills/`, `.claude/skills/`, `.cursor/skills/`, `.github/skills/`, and `.codex/skills/`. Prefer `.agents/skills/` when you want the skill to travel with the repo across compatible agents. Set `RUNFLOW_API_KEY` in the environment where your agent runs. Do not put it in `SKILL.md` or commit it to the repo. Only use a system prompt or rules file when your agent cannot read skill directories. In that case, add one short instruction: "Use the Runflow skill at https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md for Runflow integrations." ## Discovery resources | Resource | URL | Purpose | |---|---|---| | Skill | [`agent-skills/runflow/SKILL.md`](https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md) | Workflow guide for AI coding assistants. | | Skills index | [`agent-skills/index.json`](https://www.runflow.io/.well-known/agent-skills/index.json) | agentskills.io v0.2.0 manifest. | | llms.txt | [`docs.runflow.io/llms.txt`](https://docs.runflow.io/llms.txt) | This site's index for LLMs. | | llms-full.txt | [`docs.runflow.io/llms-full.txt`](https://docs.runflow.io/llms-full.txt) | Full corpus, one-shot ingestion. | | Public OpenAPI | [`docs.runflow.io/api/openapi.public.json`](https://docs.runflow.io/api/openapi.public.json) | Customer integration spec. | | API catalog | [`/.well-known/api-catalog`](https://www.runflow.io/.well-known/api-catalog) | RFC 9727 linkset. | | Models catalog | [`models-catalog.json`](https://www.runflow.io/models-catalog.json) | Full model catalog (45+ models), prices, categories. Live count via [`GET /v1/public/models`](https://api.runflow.io/v1/public/models). | | Public models API | [`GET /v1/public/models`](https://api.runflow.io/v1/public/models) | Unauthenticated catalog read. Same shape as the model details on this site. Use it for client-side pickers and discovery flows that should not ship an API key. | ## Hosts | Host | Purpose | |---|---| | `api.runflow.io` | Production API. | | `docs.runflow.io` | This site. | | `app.runflow.io` | Dashboard and per-model `llms.txt`. | | `www.runflow.io` | Marketing and agent discovery (`/.well-known/*`). | | `public.runflow.io` | Public CDN for example assets. | ## Pages in this section agentskills.io v0.2.0 detail. Connect Claude, Cursor, VS Code via Model Context Protocol. llmstxt.org convention. Spec, codegen recipes. RFC 9727 linkset. --- # llms.txt [llmstxt.org](https://llmstxt.org) is a convention: a root `/llms.txt` file with a structured link index that LLMs and agents fetch to learn what a site contains. Runflow ships two: | File | Use | |---|---| | [`/llms.txt`](https://docs.runflow.io/llms.txt) | Categorized link list. Quick site map. | | [`/llms-full.txt`](https://docs.runflow.io/llms-full.txt) | Concatenation of every doc page, frontmatter stripped. One-shot ingestion. | These files are generated from the docs corpus. Do not edit them by hand. ## Discovery Mintlify also sets the `Link` response header on every page: ```http Link: ; rel="llms-txt", ; rel="llms-full-txt" ``` Agents that follow RFC 8288 link relations find the index without crawling. ## Use it ```bash # Index curl https://docs.runflow.io/llms.txt # Full corpus curl https://docs.runflow.io/llms-full.txt ``` Pipe `llms-full.txt` into your model's context window. The full corpus is ~340 KB, well under any modern model's window. ## Two siblings Runflow also publishes: - `https://www.runflow.io/llms.txt` - marketing-side site index. - `https://www.runflow.io/llms-full.txt` - marketing-side full corpus. - `https://app.runflow.io/models/{provider}/{slug}/llms.txt` - per-model spec, including model specs that may not be public marketing pages. Use docs.runflow.io's versions if you want the developer reference. Use the others if you need marketing context or a single model's spec. ## Related Machine-readable API spec. Integration guide for agents. --- # MCP server The Runflow MCP server is a thin, stateless proxy that exposes the Runflow API to any [Model Context Protocol](https://modelcontextprotocol.io) client. Bring your own `rf_live_*` key, point your agent at `mcp.runflow.io`, and you have: - A `generate` tool that runs any Runflow model or Solution and waits for the result. - A `list_models` tool that filters the live catalog. - One `/runflow:` slash command per active Solution, auto-generated from the catalog. - The Runflow [agent skill](https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md) returned as MCP `initialize` instructions, so the Solutions-first decision rule is in context for the whole session. The MCP server is the fastest path for agents that already speak MCP. If your runtime does not speak MCP, integrate via the regular [REST API](/quickstart) — the MCP server is a convenience layer, not a different product. ## Connect in 30 seconds | Endpoint | Value | |---|---| | Server URL | `https://mcp.runflow.io/mcp` | | Transport | Streamable HTTP (JSON-RPC) | | Protocol version | `2025-06-18` | | Auth | `Authorization: Bearer rf_live_…` | | Health check | [`GET /healthz`](https://mcp.runflow.io/healthz) | Get an `rf_live_*` key from the [dashboard](https://app.runflow.io) under **Settings → API keys**. Service keys (`rf_svc_*`) are not accepted — only `rf_live_*` can dispatch runs. Claude Code, Cursor, and VS Code do **not** currently substitute `${VAR}` (or `${env:VAR}`) inside MCP HTTP-transport `headers`. The literal string is sent and the server returns 401. Until the clients ship support ([claude-code#6204](https://github.com/anthropics/claude-code/issues/6204), [#51581](https://github.com/anthropics/claude-code/issues/51581), [Cursor forum 79296](https://forum.cursor.com/t/how-to-use-environment-variables-in-mcp-json/79296)), the working paths are: paste your `rf_live_…` key directly into the config file and keep it out of version control (gitignore it, never commit it), OR use a stdio wrapper like `mcp-remote` that does honour env vars. ### Claude Code Easiest reliable path is the stdio wrapper. Add to `~/.claude/config.json`: ```json { "mcpServers": { "runflow": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.runflow.io/mcp", "--header", "Authorization:Bearer ${RUNFLOW_API_KEY}"], "env": { "RUNFLOW_API_KEY": "rf_live_…" } } } } ``` `mcp-remote` substitutes `${RUNFLOW_API_KEY}` from the `env` block before opening the HTTPS connection. If you prefer the direct HTTP transport, paste the literal key: ```json { "mcpServers": { "runflow": { "type": "streamable-http", "url": "https://mcp.runflow.io/mcp", "headers": { "Authorization": "Bearer rf_live_…" } } } } ``` Add the config file to `.gitignore` if it lives inside a project. Never commit a literal key. ### Cursor Same constraint as Claude Code. Add to `~/.cursor/mcp.json`: ```json { "mcpServers": { "runflow": { "url": "https://mcp.runflow.io/mcp", "headers": { "Authorization": "Bearer rf_live_…" } } } } ``` `~/.cursor/mcp.json` is user-scoped (not committed). If you want a project-scoped config, prefer the `mcp-remote` stdio pattern shown above so the key can live in a process env var instead of a JSON file. ### VS Code MCP Use the VS Code MCP extension's HTTP transport with URL `https://mcp.runflow.io/mcp` and a single literal-key `Authorization` header in user settings. Same env-var-substitution limitation as the CLI clients. ### Claude.ai connectors Add a custom connector with URL `https://mcp.runflow.io/mcp` and paste your bearer when prompted. The connector inherits the `initialize.instructions` SKILL.md automatically. The bearer is stored encrypted in your account, not in a local file. ## Tools ### `generate` Dispatch a run and (by default) wait for the result. ```text generate(model, input, wait?, client_ref?) ``` | Argument | Type | Notes | |---|---|---| | `model` | string | `provider/slug` as it appears in `list_models`. Nested slugs are allowed (`alibaba/wan/v2.7/text-to-video`, `runflow/object-removal/prompt`). | | `input` | object | Model-specific input. See the per-model schema at `https://docs.runflow.io/models//` or the live `llms.txt` at `https://www.runflow.io/models///llms.txt`. | | `wait` | boolean (default `true`) | When `true`, blocks until terminal status (`succeeded` / `failed` / `cancelled`), emitting `notifications/progress` between polls. When `false`, returns immediately with a `run_id`. | | `client_ref` | string (optional) | Idempotency key. Strongly recommended for runs you expect to take more than 60 seconds — see resume semantics below. | Polling backs off adaptively: 2s for the first 30s, 5s through 2 minutes, then 10s. The server enforces `MCP_POLL_TIMEOUT_MS` (10 minutes by default). If the poll exceeds that ceiling, the tool returns a `polling_timeout` envelope with the latest run state. The upstream run keeps running. #### Resuming after `polling_timeout` If you supplied `client_ref` on the original call, re-invoke `generate` with the **same** `model` and the **same** `client_ref`. Runflow honours the idempotency key and returns the existing run. Without `client_ref`, the run still completes upstream but cannot be resumed via MCP — the `run_id` is returned in the error envelope so you can poll the REST API directly. #### Example ```json { "method": "tools/call", "params": { "name": "generate", "arguments": { "model": "runflow/object-removal/prompt", "input": { "image_url": "https://example.com/photo.jpg", "prompt": "remove the watermark" } } } } ``` ### `list_models` Browse the unified catalog. Solutions (`provider_slug=runflow`) appear alongside raw provider models — no special casing. ```text list_models(category?, provider?, query?, limit?) ``` | Argument | Type | Notes | |---|---|---| | `category` | enum | `text-to-image`, `image-to-image`, `text-to-video`, `image-to-video`, `video-to-video`, `text-to-audio`, `solution` | | `provider` | string | Filter by provider slug (`runflow`, `openai`, `google`, …). | | `query` | string | Substring match on `model_name` or `model_slug`. | | `limit` | integer 1-200 | Default 50. | Each entry includes a `runs_endpoint` you can pass straight to `generate` (strip the `/v1/models/` prefix and `/runs` suffix to get the `model` argument). ## Prompts The server auto-builds one slash command per active Solution. Solutions are catalog entries with `provider_slug=runflow` — they encode a complete workflow behind a single endpoint. You will see prompts like: - `/runflow:headshots` - `/runflow:logo-inpaint` - `/runflow:object-removal-prompt` - `/runflow:smart-segmentation` - `/runflow:upscale` Selecting a prompt fills your chat with a templated message containing the Solution's required inputs. Edit the inputs, send, and the server dispatches the run through `generate` for you. Prompts are memoised per catalog snapshot, so `prompts/list` is cheap and `initialize` does not rebuild on every request. ## Auth The server accepts any bearer matching `MCP_ACCEPTED_KEY_PREFIXES` (default `rf_live`). Send the key verbatim: ```http Authorization: Bearer rf_live_… ``` 401 responses include an [RFC 9728](https://datatracker.ietf.org/doc/rfc9728/) `WWW-Authenticate` header: ```http WWW-Authenticate: Bearer realm="runflow-mcp", error="invalid_token", resource_metadata="https://mcp.runflow.io/.well-known/oauth-protected-resource" ``` The `/.well-known/oauth-protected-resource` endpoint returns a stub `resource` block in v1.0; a future minor release will add authorization-server pointers for the in-browser MCP OAuth flow. ## Errors Two distinct envelope shapes, depending on where the error originates. ### Transport-level (JSON-RPC) Returned when a request is rejected before the tool handler runs — host check, body cap, auth, rate limit. Lives at HTTP 4xx/5xx. | `error.data.code` | When | HTTP | |---|---|---| | `host_rejected` | `Host` not in the allowlist. | 403 | | `origin_rejected` | `Origin` not in the allowlist. | 403 | | `body_too_large` | POST body > `MCP_MAX_BODY_BYTES` (256 KB default). | 413 | | `missing_authorization` | No `Authorization` header. | 401 | | `malformed_authorization` | Bearer prefix not in `MCP_ACCEPTED_KEY_PREFIXES`. | 401 | | `rate_limited` | Sliding-window cap exceeded. `retry_after_seconds` included. | 429 | Default rate-limit window is 60s with caps of 30 req/IP pre-auth, 120 req/IP post-auth, and 600 req per distinct bearer fingerprint. ### Tool-result envelope Returned at HTTP 200 with `isError: true` when a tool succeeds at the transport layer but fails inside `generate` or `list_models`. Clients reading `structuredContent.error` get a stable shape: ```json { "error": { "code": "insufficient_credits", "message": "Top up to continue.", "upstream_status": 402, "next_action": "Top up the Runflow account balance, then retry.", "details": { "errors": [{ "type": "insufficient_credits" }], "path": "/v1/models/openai/gpt-image-2/runs" } } } ``` Clients that only render text get a stable `[code] message` lead line. | Code | Tool | When | |---|---|---| | `polling_timeout` | `generate` | Run exceeded `MCP_POLL_TIMEOUT_MS`. `details.run` carries the latest state. Resume by re-calling with the same `client_ref`. | | `generate_failed` | `generate` | Unclassified internal failure during dispatch or poll. | | `insufficient_credits` | `generate` | Account balance is zero or negative. Top up at `app.runflow.io`. | | `unauthorized` | `generate` | Bearer rejected by `api.runflow.io`. | | `model_not_found` | `generate` | The `provider/slug` does not match an active catalog entry. | | `catalog_fetch_failed` | `list_models` | Marketing-site catalog unreachable. | ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Tools don't appear after restart | Config file path wrong, or the agent doesn't pick up MCP changes without a full quit | Verify the path matches your agent (`~/.claude/config.json`, `~/.cursor/mcp.json`); fully quit the agent (not just close the window); re-open. | | 401 on first call | Bearer is missing, malformed, or has the wrong prefix | `echo $RUNFLOW_API_KEY` in the shell that launched the agent; key must start with `rf_live_`. Service keys (`rf_svc_`) are rejected by design. | | `host_rejected` 403 | Running a local MCP server whose `MCP_ALLOWED_HOSTS` doesn't include `localhost` | Set `MCP_ALLOWED_HOSTS=localhost:8080,127.0.0.1:8080` in the MCP server's `.env` (server-side; the shell env on the client side has no effect). Don't drop the allowlist back to `*` — it's a DNS-rebinding defense. Production `mcp.runflow.io` is unaffected. | | `polling_timeout` repeatedly on long runs | Run exceeded the 10-minute MCP poll ceiling | Supply `client_ref` on the original call, then re-invoke with the same `client_ref` to resume. For runs you know will exceed 10 min (long video), prefer `wait=false` and poll the REST API directly. | | `/runflow:*` prompts list is empty | Catalog fetch failed at `initialize` time | Hit `https://mcp.runflow.io/healthz` to confirm the server is up; check your network for outbound restrictions to `mcp.runflow.io` and `runflow.io`. | | `insufficient_credits` | Account balance is zero or negative | Top up at [`app.runflow.io`](https://app.runflow.io). The error envelope's `details.errors[].message` carries the exact remaining balance when available. | If none of the above match, run the [smoke test](#smoke-test) below — the raw JSON-RPC response usually identifies the cause. ## Smoke test Two curl steps. The first proves your bearer is accepted and the skill is returned as `instructions`; the second proves a real tool call round-trips end-to-end. ```bash # 1) initialize — verify auth + skill delivery curl -X POST https://mcp.runflow.io/mcp \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' ``` You should see `result.serverInfo.name = "Runflow"` and `result.instructions` containing the agent skill. ```bash # 2) tools/call list_models — verify a real catalog browse round-trips curl -X POST https://mcp.runflow.io/mcp \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_models","arguments":{"category":"solution","limit":5}}}' ``` You should see active Solutions (`provider_slug=runflow`) in `result.content[0].text` along with `result.structuredContent`. The exact count varies as Solutions ship and retire — check [the live catalog](/models) for the current set. ## When to use the MCP server vs. the REST API | Situation | Use | |---|---| | Your agent natively speaks MCP (Claude Code, Cursor, Claude.ai connector). | **MCP server** — one connection, prompts auto-update with the catalog. | | You are building a server-side workflow, batch job, or webhook handler. | [REST API](/quickstart) — direct, no proxy. | | You need cancellation, batches, webhooks, or admin endpoints. | [REST API](/quickstart) — MCP exposes `generate` + `list_models` only. | | You need to share one set of catalog prompts across multiple developers. | **MCP server** — every connected client sees the same `/runflow:*` set. | ## Related - [Agent skill](/agents/skills) — the SKILL.md the MCP server returns as `instructions`. - [Quickstart (REST)](/quickstart) — direct HTTP integration. - [Models](/models) — the catalog `generate` dispatches into. - [Authentication](/concepts/authentication) — bearer lifecycle, scopes, rotation. - [Errors](/concepts/errors) — REST error vocabulary. MCP envelopes use lowercase `snake_case` codes (e.g. `insufficient_credits`); the REST API returns the corresponding `SCREAMING_SNAKE` enum (e.g. `INSUFFICIENT_CREDIT`). The lowercase MCP form is a direct lowercasing of the REST `errors[].type`; treat them as the same code under different casing conventions. --- # OpenAPI Runflow publishes a public OpenAPI 3.1 spec for customer integrations and codegen: ``` https://docs.runflow.io/api/openapi.public.json ``` The public spec is what generates the [API reference](/api-reference/models/search-models) tab on this site. Use it for codegen and agent integrations. Every endpoint still requires the right Bearer token and role. A path being listed does not mean an unauthenticated caller can use it. ## Codegen recipes ### TypeScript types ```bash npx openapi-typescript https://docs.runflow.io/api/openapi.public.json -o runflow-types.ts ``` ### Python client ```bash pip install openapi-python-client openapi-python-client generate --url https://docs.runflow.io/api/openapi.public.json ``` ### Go client ```bash go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest oapi-codegen -package runflow https://docs.runflow.io/api/openapi.public.json > runflow.go ``` ### Postman / Insomnia Both import OpenAPI directly. Point them at `https://docs.runflow.io/api/openapi.public.json` for customer integrations. ## Auth One scheme: `HTTPBearer`. See [Authentication](/concepts/authentication). ## Versioning The path prefix is `/v1`. Breaking changes trigger a `/v2` path. Additive changes ship in `/v1`. ## Related No SDKs yet, codegen instead. RFC 9727 linkset. --- # Agent skills [Agent Skills](https://agentskills.io) is a convention for publishing integration instructions that AI coding assistants discover and follow. Runflow exposes one skill. ## The index ```bash curl https://www.runflow.io/.well-known/agent-skills/index.json ``` ```json { "$schema": "https://agentskills.io/schema/v0.2.0/index.json", "skills": [ { "name": "runflow", "type": "instructions", "description": "Integrate the Runflow API.", "url": "https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md", "sha256": "" } ] } ``` The `sha256` lets agents detect tampering between fetch and use. ## The skill ```bash curl https://www.runflow.io/.well-known/agent-skills/runflow/SKILL.md ``` Markdown body. Covers: API key setup, base URL, auth header, model catalog discovery, run lifecycle, callback or polling result delivery, error handling, common pitfalls. ## Install in your agent See [For agents](/agents) for the recommended install flow. Prefer installing `SKILL.md` into a project or user skill directory over pasting long instructions into a prompt. ## Why a skill, not just docs? A skill is a single, discoverable, hash-pinned document an agent fetches once. Docs are a website you navigate. Agents prefer the skill because: - One fetch, no crawl. - SHA256 makes tampering detectable. - Versioned at the URL level. - Freed from the surrounding marketing copy. Use the skill as the workflow guide. Use OpenAPI for endpoint schemas and auth, and the models catalog for model availability and pricing. If they disagree, check the machine-readable source before coding. ## Related For agents that codegen instead. For LLMs that ingest the corpus. --- # ComfyUI workflows API The ComfyUI workflows surface lives at `/v1/comfyui-workflows/*` and is intentionally **not** in the public OpenAPI bundle (the generated spec under [API reference](/api) covers the core model + run surface only). The endpoints below are reachable and supported - this page is the canonical inline reference until they ship to the public spec. For the end-to-end deploy flow, see [Deploy a ComfyUI workflow](/guides/comfyui-deploy). This page documents the bare API. All endpoints require `Authorization: Bearer $RUNFLOW_API_KEY`. `X-Organization-Id` is optional and defaults to the key's org. Reads need the `comfyui-workflows:read` scope; writes need `comfyui-workflows:create`, `:edit`, or `:delete` respectively. ## Endpoints ### Workflows | Method | Path | Purpose | |---|---|---| | `GET` | `/v1/comfyui-workflows` | Search/list workflows in your org (ANTLR filter, pagination, embeds). | | `POST` | `/v1/comfyui-workflows` | Create a new workflow (with nested inputs/outputs/GPU models). | | `GET` | `/v1/comfyui-workflows/{id}` | Fetch a workflow by UUID. | | `PATCH` | `/v1/comfyui-workflows/{id}` | Partial update. Replace graph, swap GPU mode, toggle public. | | `DELETE` | `/v1/comfyui-workflows/{id}` | Soft-delete a workflow. | | `GET` | `/v1/comfyui-workflows/{owner}/{slug}` | Fetch by canonical `{org}/{slug}` path - mirrors the dispatch route. | ### Dispatch runs | Method | Path | Purpose | |---|---|---| | `POST` | `/v1/comfyui-workflows/{owner}/{slug}/runs` | Dispatch a workflow run. Same shape as [`POST /v1/models/.../runs`](/api-reference/models/dispatch-a-run). | | `GET` | `/v1/comfyui-workflows/{owner}/{slug}/runs` | List recent runs for the workflow. | Inspect a dispatched run via [`GET /v1/runs/{id}`](/api-reference/runs/get-run) (same generic run record as model runs). ### Inputs and outputs The Runflow Input / Output nodes in the ComfyUI graph map to first-class resources you can list and manage: | Method | Path | Purpose | |---|---|---| | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/inputs` | List a workflow's inputs. | | `POST` | `/v1/comfyui-workflows/{comfyui_workflow_id}/inputs` | Add an input definition. | | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/inputs/{id}` | Fetch one. | | `PATCH` | `/v1/comfyui-workflows/{comfyui_workflow_id}/inputs/{id}` | Update display name, description, defaults. | | `DELETE` | `/v1/comfyui-workflows/{comfyui_workflow_id}/inputs/{id}` | Remove. | | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/outputs` | List outputs. | | `POST` | `/v1/comfyui-workflows/{comfyui_workflow_id}/outputs` | Add an output. | | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/outputs/{id}` | Fetch one. | | `PATCH` | `/v1/comfyui-workflows/{comfyui_workflow_id}/outputs/{id}` | Update. | | `DELETE` | `/v1/comfyui-workflows/{comfyui_workflow_id}/outputs/{id}` | Remove. | ### Reference and stats | Method | Path | Purpose | |---|---|---| | `GET` | `/v1/comfyui-workflows/categories` | List flow categories (reference data). | | `GET` | `/v1/comfyui-workflows/categories/{code}` | One category. | | `GET` | `/v1/comfyui-workflows/{owner}/{slug}/evaluation-issue-categories` | Distinct (category, subcategory) pairs surfaced by evaluations for a workflow. | | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/evaluation-stats` | Aggregate evaluation outcomes over a date range. | | `GET` | `/v1/comfyui-workflows/{comfyui_workflow_id}/run-performance-stats` | Aggregate run performance (latency, completion, failures) over a date range. | ## Dispatch a run `POST /v1/comfyui-workflows/{owner}/{slug}/runs` takes the same envelope you use for first-party models: ``` curl -X POST "https://api.runflow.io/v1/comfyui-workflows/{owner}/{slug}/runs" \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "a sea otter holding a smooth pebble", "width": 1024, "height": 1024 }, "callback_url": "https://your-server.com/webhook", "client_ref": "ticket-4821" }' ``` | Field | Type | Required | Notes | |---|---|---|---| | `input` | object | yes | Keyed by your workflow's Runflow Input node `input_id`s. File slots accept `runflow://assets/{uuid}` or any `https://` URL. | | `callback_url` | string \| null | no | POSTed once the run reaches a terminal state. See [Callbacks](/concepts/callbacks). | | `client_ref` | string \| null | no | Max 255 chars. Echoed in webhook payloads. **Not** an idempotency key; not sent to the worker. | | `metadata` | object \| null | no | Arbitrary key/value attached to the run record. | Response is the minimal `{ "run_id": "...", "status": "queued" }` envelope, not the full run record. Poll [`GET /v1/runs/{id}`](/api-reference/runs/get-run), whose `status_code` advances to a terminal state, or wait for the callback. ## Create a workflow programmatically Most teams use the [ComfyUI-Runflow plugin](/guides/comfyui-deploy) to create workflows from a live ComfyUI install. For automation or CI, you can POST directly: ``` curl -X POST "https://api.runflow.io/v1/comfyui-workflows" \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Background swap v2", "slug": "background-swap-v2", "workflow_json": { "...": "the full ComfyUI graph JSON" }, "description": "Replace product background, preserve shadow.", "category_code": "image-to-image", "gpu_mode": "auto", "is_public": false }' ``` `name` and `workflow_json` are the only required fields. The full create validator accepts nested `inputs[]`, `outputs[]`, `gpu_explicit_models[]`, `custom_nodes[]`, `models[]`, `environment`, and `resources` arrays. ## Soft delete `DELETE /v1/comfyui-workflows/{id}` marks the workflow deleted but preserves it for audit and rollback. Deleted workflows reject new runs (`404` on the dispatch endpoint) but keep their historical run records reachable via the generic [`GET /v1/runs/{id}`](/api-reference/runs/get-run). ## Related End-to-end plugin walkthrough. Run record shape, polling, callbacks. Org-level event delivery. Public model + run surface (OpenAPI). --- # Authentication Every Runflow API request needs an `Authorization: Bearer ` header. Requests without a valid token return `401 Unauthorized`. ## Header ```http Authorization: Bearer YOUR_API_KEY ``` ## Get a key 1. Sign in at [app.runflow.io](https://app.runflow.io/settings/api-keys). 2. Open **Settings -> API Keys**, click **Create**. 3. Copy the token. It is shown once. 4. Save it as `RUNFLOW_API_KEY` in your secret manager. ## Example ```bash export RUNFLOW_API_KEY="your_token_here" curl https://api.runflow.io/v1/auth/me \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` A `200` with your account payload confirms the key works. ## Rotate Compromised key? Revoke it from the dashboard, create a new one, deploy. No downtime if you keep both keys live during cutover. ## Best practices - Read the token from an env var or secret manager. Never commit it. - Use distinct keys per environment so a leak in staging cannot touch prod. - Call the API from your backend, not the browser. - Watch for `401` spikes. They usually mean a leaked or rotated key. ## Related Status codes and the error envelope. Make your first authenticated call. --- # Callbacks Long-running models push their final payload to a URL you control. You provide the URL on the request; Runflow POSTs the result when the run finishes. ## Pattern 1. Call `POST /v1/models/{model_id}/runs` with `callback_url`, where `{model_id}` is the slash-delimited provider/model path shown on the model page. 2. Save the returned `id`. Callback payloads call the same value `run_id`. 3. Run finishes in the background. 4. Runflow POSTs to `callback_url`. 5. Your handler returns `200 OK` within a few seconds. ## Callback payload The callback body is an event envelope, frozen at delivery time: ```json { "event": "run.completed", "run_id": "01J0...", "status": "succeeded", "output": { "image_urls": ["https://..."] }, "duration_ms": 102345, "created_at": "2026-05-06T10:00:00.000+00:00", "completed_at": "2026-05-06T10:01:42.000+00:00", "metadata": null } ``` | Field | Notes | |---|---| | `event` | One of `run.completed`, `run.failed`, `run.cancelled`, `run.partial_succeeded`. Event names use UK spelling (`cancelled`, two `l`s); the run's `status_code` field uses US spelling (`canceled`). Both literals are stable. | | `run_id` | UUIDv7 of the run. Match against the `id` from your `POST /runs` response. | | `status` | Terminal callback status. Run records expose the same value as `status_code` on [`GET /v1/runs/{id}`](/api-reference/runs/get-run). | | `output` | The normalized model output on success, an error envelope on failure, or `null` if cancelled. Batch callbacks use a self-describing `{items, succeeded, failed, cancelled, total}` envelope, see the OpenAPI for the full shape. | | `duration_ms` | Total run wall-clock time. | | `created_at`, `completed_at` | ISO 8601, `+00:00` offset (not `Z`) so HMAC verification stays byte-equivalent. | | `metadata` | Whatever you passed at run creation, verbatim. | Output URLs are presigned and time-limited. Download or re-upload them to your own storage on receipt. Batch callbacks ship the same envelope but with `batch_id` instead of `run_id`. ## Verify the source Create a callback secret and Runflow signs every callback with HMAC-SHA256: ```bash curl -X POST https://api.runflow.io/v1/callback-secrets \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"label": "production"}' ``` Save the returned `plain_secret`. Runflow then sends `Runflow-Signature: ` on every callback (alongside a `Runflow-Request-Id` for log correlation). See [Verify callback signatures](/guides/verify-callback-signatures) for handler code. ## Retries Runflow retries failed callbacks (non-2xx response, timeout, connection error) on an exponential schedule. Inspect attempts with [`GET /v1/runs/{run_id}/callback`](/api-reference/runs/get-callback-delivery-history). ## Manual redelivery Replay a callback at any time: ```bash curl -X POST https://api.runflow.io/v1/runs/{run_id}/callback-redeliveries \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` ## Local development Your laptop is not reachable from the public internet. Tunnel it: ```bash ngrok http 3000 # use the https://abc123.ngrok-free.app URL as callback_url ``` ## Related HMAC verify in Node and Python. History, redelivery, and secrets. --- # Clients Runflow ships an official JavaScript surface as [`runflow-js`](https://github.com/runflow-io/runflow-js). Three packages, one for each integration shape. For other languages, generate a client from the public OpenAPI spec. ## JavaScript packages | Package | What it does | Where it runs | |---|---|---| | [`@runflow-io/sdk`](/guides/javascript-sdk) | Typed HTTP client and tool DSL for the API. | Node, Bun, Deno, browsers, edge workers. | | [`@runflow-io/proxy`](/guides/javascript-sdk#proxy-browser-calls-server-side) | Web Standards handler that injects your API key for browser calls. | Any server framework (Next.js, Hono, Workers, Express). | | [`@runflow-io/studio`](/guides/embed-studio) | Drop the Studio UI into a `
`. | Browser, via npm. | All three packages target the same `api.runflow.io` REST surface documented in the [API reference](/api). They are MIT-licensed and published from [github.com/runflow-io/runflow-js](https://github.com/runflow-io/runflow-js). **Browsing without a key.** [`GET /v1/public/models`](https://api.runflow.io/v1/public/models) is an unauthenticated endpoint that returns the same model catalog the docs render from. Useful for client-side model pickers, IDE plugins, and discovery flows that should not ship an API key. Run dispatch + everything else still requires `Authorization: Bearer $RUNFLOW_API_KEY`. These packages are pre-1.0 (`0.0.x`). Patch releases may change types or runtime behavior. Pin exact versions in production until 1.0. ### Install ```bash bun add @runflow-io/sdk # or: npm install @runflow-io/sdk # or: pnpm add @runflow-io/sdk ``` ### One call, server-side ```ts import { Runflow } from "@runflow-io/sdk"; const rf = new Runflow({ apiKey: process.env.RUNFLOW_API_KEY }); const dispatched = await rf.models.run("runflow/background-removal", { input: { image_url: "https://example.com/photo.jpg" }, }); // dispatched.id is the run id; same shape as the REST response. const final = await rf.runs.wait(dispatched.id); console.log(final.output); ``` Walk through every option in the [JavaScript SDK guide](/guides/javascript-sdk). ## Codegen for other languages The public OpenAPI 3.1 spec is the same one that generates this site's API reference: ``` https://docs.runflow.io/api/openapi.public.json ``` ### TypeScript types ```bash npx openapi-typescript https://docs.runflow.io/api/openapi.public.json \ -o src/runflow-types.ts ``` ### Python client ```bash pip install openapi-python-client openapi-python-client generate --url https://docs.runflow.io/api/openapi.public.json ``` ### Go client ```bash go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest oapi-codegen -package runflow https://docs.runflow.io/api/openapi.public.json > runflow.go ``` ## Hand-rolled HTTP For most integrations a raw fetch or `requests` call is enough. Both snippets bound the network wait so a stuck connection never hangs the caller. ```python Python import os, requests RUNFLOW_API_KEY = os.environ["RUNFLOW_API_KEY"] REQUEST_TIMEOUT = 30 # seconds def runflow_post(path, body): r = requests.post( f"https://api.runflow.io{path}", headers={"Authorization": f"Bearer {RUNFLOW_API_KEY}"}, json=body, timeout=REQUEST_TIMEOUT, ) r.raise_for_status() return r.json() ``` ```javascript Node.js const RUNFLOW_API_KEY = process.env.RUNFLOW_API_KEY; const REQUEST_TIMEOUT_MS = 30_000; async function runflowPost(path, body) { const r = await fetch(`https://api.runflow.io${path}`, { method: "POST", headers: { "Authorization": `Bearer ${RUNFLOW_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify(body), signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), }); if (!r.ok) throw new Error(`${r.status} ${await r.text()}`); return r.json(); } ``` ## Related Server-side, browser, and the tool DSL. Drop the Studio into your site in three lines. Spec details and codegen recipes. Bearer header. --- # Errors ## Envelope Errors return a JSON body with `code`, `message`, and optional `errors[]`: ```json { "code": "PERMISSION_DENIED", "message": "Your account is not authorized to access this resource.", "errors": null } ``` | Field | Type | Notes | |---|---|---| | `code` | string | Machine-friendly identifier. Stable across releases. | | `message` | string | Human-friendly message. May change. | | `errors[]` | array | Field-level details for `422 Unprocessable Entity`. | ## Status codes | Code | Meaning | Action | |---|---|---| | `400` | Bad request shape. | Check your JSON body against the spec. | | `401` | Missing or invalid token. | Confirm `Authorization: Bearer ` is present and the key is active. | | `402` | Payment required. | Check the balance at [`GET /v1/billing/balance`](/api-reference/account/get-organization-credit-balance), then top up from [`app.runflow.io/settings/billing`](https://app.runflow.io/settings/billing). | | `403` | Permission denied or email not verified. | Check the `code` for `PERMISSION_DENIED` vs `EMAIL_NOT_VERIFIED`. | | `404` | Resource not found. | Verify the path and the resource ID. | | `409` | Conflict. | Usually a duplicate create or a race. Refetch and retry. | | `422` | Validation error. | Read `errors[]` for per-field details. | | `503` | Service unavailable. | Backoff and retry with jitter. Check [`/v1/readiness`](https://api.runflow.io/v1/readiness) for component status, [contact support](https://cal.com/team/runflow/talk-to-founders?duration=25) if persistent. | ## Common codes | `code` | Cause | |---|---| | `UNAUTHORIZED` | Bad or missing token. | | `PERMISSION_DENIED` | Token lacks scope for this operation. | | `EMAIL_NOT_VERIFIED` | User has not confirmed their email. | | `INSUFFICIENT_CREDIT` | Account balance is too low. Top up. | | `RUN_NOT_FOUND` | The `run_id` does not exist or belongs to another org. | | `MODEL_UNAVAILABLE` | Provider downtime. Retry or pick a sibling model. | ## Run failures When `POST /v1/models/.../runs` succeeds but the run later fails, the run object carries `failure_code`, `failure_stage`, and `failure_message`: ```json { "id": "01J0...", "status": "failed", "failure_code": "INPUT_VALIDATION_FAILED", "failure_stage": "preprocess", "failure_message": "image dimensions exceed 4096x4096" } ``` ## Retry strategy - `5xx` and `429`: exponential backoff with jitter, max 5 attempts. - `4xx` other than `429`: do not retry, fix the request. - Network errors: same as `5xx`. ## Related 401 and Bearer header detail. 402 and credit balance. --- # Latency Run latency varies by model category and by the size/complexity of the input. Use the ballparks below to size your client behavior; for actual measurements per model, see [Per-model performance](#per-model-performance). ## Ballparks by category | Category | Typical (p50) | Tail (p95) | What drives it | |---|---|---|---| | `text-to-image` | 4–15 s | 30 s | Image model + resolution + step count. 1K vs 4K is the biggest factor. | | `image-to-image` | 8–30 s | 60 s | Edit-style models (Nano Banana Pro Edit, GPT Image 2 Edit) sit at the lower end. Workflow Solutions that chain models (object-removal, reference-inpaint, background-replace) sit higher because they run multiple steps + an evaluator. | | `text-to-video` | 60–300 s | 600 s | Duration × resolution × model. A 5 s 1080p Wan run is ~90 s; a 15 s 4K Veo or HeyGen run can push 5 min. | | `image-to-video` | 60–300 s | 600 s | Same drivers as text-to-video plus reference processing time. | | `video-to-video` | 90–600 s | 900 s | Wan video edit, Happy Horse video edit. Heaviest in the catalog. | | `text-to-audio` | 3–10 s | 20 s | ElevenLabs v3 TTS, Gemini TTS. Sub-10 s for short utterances. | These are full lifecycle measurements: `queued` → `dispatching` → `running` → `succeeded` as observed via `GET /v1/runs/{id}`. Network round trips not included. Use the **p95 column** to size client-side timeouts, not p50. A timeout below p95 will produce false-positive failures on long-tail runs that would have succeeded. ## What this means for your client | Decision | Recommendation | |---|---| | **Polling interval** | 2 s for image categories, 10 s for video. Polling faster wastes API quota without changing your latency. | | **Skeleton / "generating" UI** | Show a progress affordance based on the category's p50, not a hardcoded value. A user who waits 4 s for a text-to-image run gets a snappier feel than the same user waiting 4 s for a text-to-video. | | **HTTP request timeout** | At least 2× the p95 for the category, or use a callback URL ([Callbacks](/concepts/callbacks)) to avoid client timeouts entirely. | | **Retry strategy** | Don't retry the same run on timeout. That creates a duplicate. Use `client_ref` for idempotency, or just poll longer. See [Errors](/concepts/errors) for the retry table. | | **User-facing copy** | Rotate between phrases at intervals matched to category p50 (`"Generating..."` → `"Touching up..."` → `"Almost there..."`). Static `"Loading..."` for a 4-minute video run is bad UX. | ## Why we don't publish per-model latency hints (yet) A `p50_seconds` / `p95_seconds` field on `GET /v1/public/models` is on the [public catalog discoverability plan](https://github.com/bettergroupinc/runflow-monorepo/blob/main/docs/exec-plans/active/2026-05-23-public-catalog-discoverability.md#scope) (Phase D). Until that ships, use the category ballparks above. If you need per-model precision, the authenticated `/v1/models/{id}/run-performance-stats` endpoint returns aggregated stats from your org's recent runs. ## Solutions that include output evaluation Some Solutions run a quality evaluation step on the output before returning. That step adds 2-4 minutes to the total wall-clock time on top of the underlying model run. Read each solution's page at [www.runflow.io/api](https://www.runflow.io/api) for whether the solution evaluates output before returning, and use `(category p95) + 4 min` to size timeouts for solutions that do. ## Related Lifecycle, statuses, output shape. Skip polling entirely for long runs. Quota and back-off rules. Retry table per status code. --- # Pricing Each model has a `price_label` shown on its [model page](/models). The label tells you the unit: - `$0.35/request` - flat per call. - `$0.14/second` - per output second (video models). - `$0.03/megapixel` - per output megapixel (image models). - `$0.075/image` - per output image (multi-image models). Prices come from the [models catalog](https://www.runflow.io/models-catalog.json). The [`/models`](/models) page is generated from that catalog and updates when the docs rebuild. ## Credits Runflow uses a credit balance. Every successful run debits credits at the model's rate. Failed runs are not billed. Check your balance: ```bash curl https://api.runflow.io/v1/billing/balance \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Response: ```json { "balance_cents": 12450, "currency": "USD" } ``` Top up at [app.runflow.io/settings/billing](https://app.runflow.io/settings/billing). ## Auto-refill Configure a card to auto-charge when the balance drops below a threshold. Done from the dashboard. ## When you run out The next run returns `402 Payment Required` with `code: INSUFFICIENT_CREDIT`. Existing in-flight runs finish. ## Quotes For volume pricing, an enterprise contract, or non-standard payment terms, [talk to us](https://cal.com/team/runflow/talk-to-founders?duration=25). ## Related Browse prices per model. 402 and INSUFFICIENT_CREDIT. --- # Rate limits The API enforces a default request-rate limit, sized for typical production load. Exceed it and the call returns `429 Too Many Requests` with the standard error envelope: ```json { "code": "RATE_LIMITED", "message": "Rate limit exceeded: ..." } ``` 429 responses do not currently carry rate-limit headers, so do not branch on `Retry-After` or `X-RateLimit-*`. Treat a `429` as the signal to slow down, and retry with backoff. ## Backoff Retry on `429` with exponential backoff plus jitter. Pseudocode: ```python import random, time delay = 1 for attempt in range(5): response = call_runflow() if response.status != 429: break time.sleep(min(delay, 32) + random.uniform(0, 1)) delay = min(delay * 2, 32) ``` ## Run capacity Runs are dispatched asynchronously onto a shared worker fleet. When capacity is busy, a run waits in the `queued` status until a worker is free rather than being rejected, so a sustained backlog shows up as runs sitting in `queued` (see [Runs](/concepts/runs) for the lifecycle). If queue times grow beyond what your workload can tolerate, get in touch about higher throughput. ## Need a higher limit? [Talk to us](https://cal.com/team/runflow/talk-to-founders?duration=25). Include peak QPS, average payload size, and target concurrency. ## Related All status codes including 429. Per-request and per-second pricing. --- # Requests ## Base URL ``` https://api.runflow.io ``` All endpoints live under `/v1/`. The public OpenAPI spec is at [`docs.runflow.io/api/openapi.public.json`](https://docs.runflow.io/api/openapi.public.json). ## Content types | Method | Content-Type | Use | |---|---|---| | `GET` | n/a | Read endpoints. | | `POST`, `PATCH`, `PUT` | `application/json` | All mutations. | | `POST /v1/asset-uploads/{id}/confirmations` | `application/json` | After uploading the file blob to the presigned URL. | ## Pagination List endpoints return cursors, not page numbers. Pass `pq` (the cursor) and `limit` (page size, max 500). ```bash curl https://api.runflow.io/v1/runs?limit=50 \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` The response includes `next_pq` and `prev_pq`. Pass `from_prev=true` to walk backward. ## Filtering List endpoints accept a filter expression in `q`: ```bash curl 'https://api.runflow.io/v1/runs?q=status_code:succeeded%20AND%20created_at>2026-04-01' \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Operators: `=`, `:`, `>`, `<`, `>=`, `<=`, `AND`, `OR`, `NOT`. String values are case-sensitive. ## Field projection Trim the response to the fields you need with `fields`: ```bash curl 'https://api.runflow.io/v1/runs?fields=id,status_code,model_id' \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` ## Sorting ```bash curl 'https://api.runflow.io/v1/runs?sort_by=created_at:desc' \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Multi-field sort: `sort_by=status_code:asc,created_at:desc`. ## Idempotency Mutations are not idempotent by default. To retry safely, save the response of the first call and skip on duplicate. ## Related Status codes and the error envelope. Callbacks, polling, statuses. --- # Runs A **run** is one execution of a model. You create runs with `POST /v1/models/{model_id}/runs`. The `model_id` segment is the model's `provider/slug` and may be more than two segments deep (e.g. `bytedance/seedance/2.0/fast/text-to-video`). Each run has an `id`, a `status_code`, optional logs, and optional node-runs (steps inside a multi-step model). ## Create a run ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "..." }, "callback_url": "https://your-server.com/webhook" }' ``` The response returns immediately with the run's `id` and `status_code: queued`. Save the `id`; that's what every other run endpoint takes. ## Lifecycle | `status_code` | Meaning | |---|---| | `queued` | Accepted, waiting for compute. | | `dispatching` | Being submitted to the execution provider. | | `running` | Executing. | | `succeeded` | Finished, outputs ready. | | `failed` | Error during execution. See `failure_message`. | | `canceled` | You canceled it. | | `partial_succeeded` | Terminal. Multi-output Solutions (and batches) use it when some but not all outputs landed. Treat the same as `succeeded` for polling-loop termination; inspect `outputs[]` to see which entries are present. | Full enum: [`GET /v1/runs/statuses`](/api-reference/runs/list-run-statuses). The `status_code` literal is US English (`canceled`, one `l`). The matching callback event name uses UK English (`run.cancelled`, two `l`s), and the batch envelope's `cancelled` count likewise uses two `l`s. Match the upstream surface exactly when filtering on either. ## Get the result Runs are asynchronous by default. Decide per request whether to receive the final payload by callback or by polling. - **Callback (recommended).** Provide `callback_url` on the create-run request. Runflow POSTs the event envelope there when the run reaches a terminal state. See [Callbacks](/concepts/callbacks). - **Polling.** Omit `callback_url`. Poll [`GET /v1/runs/{id}`](/api-reference/runs/get-run) until `status_code` is terminal. Some models also expose a model-specific `sync_mode` input for inline output. Use it only when the individual [model page](/models) documents `sync_mode` and you intentionally want the response body to carry media data. It is per-model, not platform-wide. ## Output shape On a `succeeded` run, `output` is the normalized payload, not the raw provider response. Code against the normalized shape; do not write fallback chains for provider-specific fields. ```json { "outputs": [ { "type": "image", "url": "https://public.runflow.io/runs/.../0.png", "width": 1024, "height": 1024 } ] } ``` `type` is one of `image | video | audio`. Every entry has a `url` (always present, always non-empty) and may carry additional fields like `width`, `height`, `duration`, `content_type`, `size_bytes` depending on the model. A handful of legacy provider responses still surface alongside the normalized array during the canonical metadata rollout. Plan for the normalized shape; treat anything else as a forward-compatibility bug to file rather than a fallback path to wire. On `failed`, `output` is null and the run object carries `failure_code`, `failure_stage`, `failure_message` (see [Errors](/concepts/errors)). On `partial_succeeded`, `output.outputs[]` contains the entries that did land. ## Inspect a run ```bash curl https://api.runflow.io/v1/runs/{id} \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Other reads: - [`GET /v1/runs/{id}/logs`](/api-reference/runs/search-run-logs) - structured log lines. - [`GET /v1/runs/{id}/node-runs`](/api-reference/runs/search-flow-node-runs) - per-step records for multi-step models. - [`GET /v1/runs/{id}/callback`](/api-reference/runs/get-callback-delivery-history) - the event-envelope payload Runflow delivered (or attempted to deliver) to your `callback_url`. Same shape documented under [Callbacks](/concepts/callbacks). ## Related Async result delivery. Run a model on many inputs in one request. Failed run shape. --- # Webhooks A **webhook** is an org-level subscription to one or more event types. Configure the URL once; Runflow delivers every matching event from any run in the organization. Use webhooks when you want a single endpoint to receive all activity instead of setting `callback_url` per run. ## Webhooks vs `callback_url` | Mechanism | Scope | Where it's set | When to use | |---|---|---|---| | `callback_url` (per run) | One run | On `POST /v1/models/{model}/runs` | One-off pipelines, ephemeral workers, ngrok during dev. See [Callbacks](/concepts/callbacks). | | Webhooks (org-level) | All runs in the org | `POST /v1/webhooks` once | Production. Consistent endpoint, signed deliveries, retry history, redelivery. | Both deliver the same `run.completed` / `run.failed` / `run.cancelled` payload shape documented under [Callbacks](/concepts/callbacks#callback-payload). Pick `callback_url` if you don't need history; pick webhooks if you do. ## Endpoints | Method | Path | Purpose | |---|---|---| | `GET` | `/v1/webhooks` | [List webhooks](/api-reference/webhooks/search-webhooks) for the org. | | `POST` | `/v1/webhooks` | [Create a webhook](/api-reference/webhooks/create-webhook). | | `GET` | `/v1/webhooks/{id}` | Fetch a webhook by id. | | `PATCH` | `/v1/webhooks/{id}` | Update url, secret, or event subscriptions. | | `DELETE` | `/v1/webhooks/{id}` | Delete a webhook. | | `GET` | `/v1/webhooks/event-types` | Reference list of every event you can subscribe to. | | `GET` | `/v1/webhooks/event-types/{code}` | Detail for one event type. | | `GET` | `/v1/webhooks/{webhook_id}/deliveries` | Delivery history (pass `any` as `webhook_id` to span all webhooks). | | `GET` | `/v1/webhooks/{webhook_id}/deliveries/{id}` | One delivery, with the recorded payload. | | `GET` | `/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/attempts` | Per-attempt records (timestamps, HTTP status, response). | | `GET` | `/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/attempts/{id}` | One attempt. | Every endpoint is `Authorization: Bearer $RUNFLOW_API_KEY`. The `X-Organization-Id` header is optional and defaults to the key's org. ## Create a webhook `POST /v1/webhooks` takes three required fields plus an optional list of event subscriptions: ```bash curl -X POST https://api.runflow.io/v1/webhooks \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/runflow", "secret_ciphertext": "", "secret_fingerprint": "abcd", "event_subscriptions": [ { "event_type_code": "run.completed" }, { "event_type_code": "run.failed" } ] }' ``` | Field | Type | Required | Notes | |---|---|---|---| | `url` | string | yes | HTTPS endpoint Runflow POSTs to. Max 2048 chars. | | `secret_ciphertext` | string | yes | Org-managed encrypted signing secret. Used to HMAC the payload. | | `secret_fingerprint` | string | yes | 4-char display fingerprint for the dashboard. | | `event_subscriptions[]` | array | no | One subscription per event type. Add or remove later via PATCH. | Subscribing to zero events creates a webhook that never fires. Subscribe to at least one event type from `GET /v1/webhooks/event-types`. ## Event types `GET /v1/webhooks/event-types` returns every code Runflow can emit, with the schema each event carries. Common ones today: | Code | Fired when | |---|---| | `run.completed` | A run reached `succeeded`. | | `run.failed` | A run reached `failed`. | | `run.cancelled` | A run was cancelled (admin or auto). | | `run.partial_succeeded` | Batch run finished with at least one failed item. | Event names use UK spelling (`cancelled`, two `l`s); the run's `status_code` field uses US spelling (`canceled`). Both literals are stable. ## Deliveries and attempts Every event that matches a webhook produces a **delivery**. Each delivery has one or more **attempts** (retries on 5xx, timeouts, or network errors). ```bash # List recent deliveries for one webhook curl https://api.runflow.io/v1/webhooks/{webhook_id}/deliveries \ -H "Authorization: Bearer $RUNFLOW_API_KEY" # Across every webhook in the org curl https://api.runflow.io/v1/webhooks/any/deliveries \ -H "Authorization: Bearer $RUNFLOW_API_KEY" # One delivery with its attempts curl https://api.runflow.io/v1/webhooks/{webhook_id}/deliveries/{delivery_id} \ -H "Authorization: Bearer $RUNFLOW_API_KEY" # Per-attempt detail (HTTP status, response_body, next_retry_at) curl https://api.runflow.io/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/attempts \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Each attempt records `status_code`, `response_time_ms`, `response_body`, `succeeded`, and `next_retry_at`. Use them to debug 5xx storms or a misconfigured receiver. ## Verify the signature Runflow signs every webhook delivery with HMAC-SHA256 of the raw body using your webhook's secret. The signature ships in the `Runflow-Signature` header alongside `Runflow-Request-Id` for log correlation. The verification code is the same as for per-run callbacks - see [Verify callback signatures](/guides/verify-callback-signatures). ## Receiver checklist - Return `2xx` within a few seconds. Runflow treats non-2xx, timeouts, and network errors as failures and retries on an exponential backoff. - Verify `Runflow-Signature` before trusting the body. - Be idempotent on `delivery.id` and `run_id`. Retries can deliver the same event more than once. - Log `Runflow-Request-Id`. It is the join key against `GET /v1/webhooks/{webhook_id}/deliveries`. - Use [`/v1/webhooks/any/deliveries`](/api-reference/webhooks/search-webhook-deliveries) to triage when you're not sure which webhook fired. ## Related Use `callback_url` for one-off runs. HMAC verification in Node and Python. Status codes and the error envelope. Full endpoint reference. --- # Batch process media ## What you'll do Process a folder of product shots through one model in a single call. One callback fires when every item is done. ## Prerequisites - A Runflow API key. [Create one.](https://app.runflow.io/settings/api-keys) - The model you want to run. Pick one from [`/models`](/models). - A list of input URLs or asset IDs. ## Steps `POST /v1/models/{model_id}/batches` with an array of items, where `{model_id}` is the slash-delimited provider/model path shown on the model page. Each item is a separate run. ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/batches \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "input": { "image_url": "https://...01.jpg", "prompt": "white studio" } }, { "input": { "image_url": "https://...02.jpg", "prompt": "white studio" } }, { "input": { "image_url": "https://...03.jpg", "prompt": "white studio" } } ], "callback_url": "https://your-server.com/webhook" }' ``` Response: ```json { "id": "01J0...", "status_code": "queued", "items_total": 3 } ``` Either poll [`GET /v1/batches/{id}`](/api-reference/batches/get-batch-detail) or wait for the callback. Batch records expose `status_code`; terminal callback payloads expose the same value as `status`. Batches can also finish as `partial_succeeded`. [`GET /v1/batches/{id}/items`](/api-reference/batches/list-batch-items-with-live-execution-state) returns one entry per input with the resolved `run_id`, `status_code`, and outputs. ## Verify it worked ```json { "id": "batch_01J0...", "status_code": "succeeded", "items_total": 3, "items_succeeded": 3, "items_failed": 0 } ``` `items_failed: 0`? You're done. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Some items fail, others succeed | Per-input validation error | Read each failed item's `failure_message`. | | Whole batch fails fast | Auth or quota | Check the [balance](/concepts/pricing). | | Callback never arrives | URL not reachable | See [Callbacks: local development](/concepts/callbacks). | ## Related Single-input lifecycle. Async result delivery. --- # Cancel a run **Admin-only endpoint.** `POST /v1/admin/runs/{run_id}/cancellation` requires a service key (`rf_svc_*`) with admin scope. Inference keys (`rf_live_*`) get `401`. There is intentionally no public per-run cancel. ## Why there's no public cancel Almost every Runflow model completes in under 60 seconds. By the time a caller decides to cancel, the run is usually already finishing or about to bill anyway. Adding a public cancel surfaces race conditions (cancel-after-success) and partial-charge ambiguity without saving meaningful time or money. So the API ships without one. The admin endpoint exists for **operational override**: shutting down a stuck run, responding to an abuse report, or honoring a customer ticket that asks for one specific run to be killed. ## When to use it - **A run is stuck in `queued` or `running` longer than the model's typical envelope** and the customer needs the slot freed for retries. - **You are an org admin processing a support ticket** asking to cancel a specific run. - **You are responding to an abuse pattern** and want to terminate the offending run while you investigate the key. If you're a normal API caller and just want to stop polling, drop the run; the in-flight inference will complete on its own and any callback will silently fail at your end. Cancel adds no value in that case. ## Behavior `POST /v1/admin/runs/{run_id}/cancellation` atomically transitions a non-terminal run to `cancelled` and queues a `run.cancelled` audit side effect. - **Idempotent on terminal runs.** Hitting it on an already-`succeeded` / `failed` / `cancelled` run returns the run unchanged with `200 OK`. No audit log entry, no callback re-fire. - **`201 Created`** on a real cancellation transition. The status-code difference lets audit-tailers distinguish "I caused this" from "someone got there first." - **Does not abort the in-flight inference.** The compute job keeps running on the provider side. Late callbacks land on the now-cancelled run and are silently dropped by the terminal-state guard. - **Billing follows the underlying job.** Cancellation does not auto-refund; the run bills whatever the provider charged for the work performed up to the cancel. ## Request ``` curl -X POST "https://api.runflow.io/v1/admin/runs/{run_id}/cancellation" \ -H "Authorization: Bearer $RUNFLOW_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "customer ticket #4821 - duplicate submission" }' ``` Body is optional; the only field is `reason`: | Field | Type | Required | Notes | |---|---|---|---| | `reason` | string \| null | no | 3-500 chars. Stored in `audit_log.meta.reason`. Strongly encouraged for cross-org admin overrides so the audit trail tells the story later. | ## Response Both `200` and `201` return the post-cancel `Run` object (same shape as `GET /v1/runs/{id}`). Inspect `status_code` to confirm: ```json { "id": "01J0...", "status_code": "canceled", "model": "runflow/background-removal", "completed_at": "2026-05-23T10:42:11.000+00:00" } ``` Note the spelling: run records use US `canceled`; webhook event names use UK `cancelled`. Both literals are stable. ## Errors | Code | Cause | Action | |---|---|---| | `401` | Missing or non-admin key. | Use a service key (`rf_svc_*`) with admin scope. Inference keys are rejected. | | `403` | `PERMISSION_DENIED` or `EMAIL_NOT_VERIFIED`. | Check the `code` field in the response body. | | `404` | Run id not found in your reachable orgs. | Confirm the id and the org-scope of your admin key. | | `409` | State invariant violated (e.g. `self_action_forbidden`). | Inspect `errors[0].type`. | | `422` | `reason` failed validation (under 3 chars or all whitespace). | Send a longer reason or omit the field. | ## Related Lifecycle, statuses, polling. Note: late callbacks on cancelled runs are dropped. Service vs inference keys. Status codes and error envelope. --- # ComfyUI Deploy FAQ Quick answers to the questions we hear most about ComfyUI Deploy. For the full walkthrough (install, configure, deploy, call), see [Deploy a ComfyUI workflow](/guides/comfyui-deploy). For the bare API, see [ComfyUI workflows API](/api/comfyui-workflows). **Beta.** ComfyUI Deploy is in beta. Access to create and run deployed workflows is enabled per organization (see [How do I get access?](#access-and-beta)). The plugin, API surface, and pricing are stable but may still change; breaking changes are called out in [the changelog](https://github.com/runflow-io/ComfyUI-Runflow/releases) before they ship. ## Access and beta Not yet. It is in beta, and creating or running deployed workflows is enabled per organization. The ComfyUI workflows area is visible to everyone in the dashboard, but an organization has to be enrolled before it can deploy or dispatch runs. Open the ComfyUI workflows section in [the dashboard](https://app.runflow.io). If your organization is not enrolled yet, you will see a beta gate with a **Request Beta Access** button. Click it to register your interest. The Runflow team enrolls organizations from there; once your organization is enrolled the page shows your workflows and the API stops returning the beta error. Creating a workflow and dispatching a run are gated on your organization being enrolled in the beta. Until then, both return `403` with the error code `COMFYUI_DEPLOY_BETA_NOT_ENABLED`. Read endpoints (listing and fetching workflows) are not gated. The gate also applies when you try to run another organization's public workflow, so being able to see a workflow does not by itself let you run it. Request access in the dashboard, and the same calls will succeed once you are enrolled. Yes. Beta access is granted per organization, so every member of an enrolled organization can use ComfyUI Deploy. Any of your organization's API keys with the right scopes can deploy and call endpoints, and runs bill to the organization regardless of which member or key triggered them. ## Install and setup ComfyUI loaded before the plugin was on disk, the plugin landed in the wrong `custom_nodes/` directory, or you only refreshed the browser tab. A browser refresh is not enough: the Python process has to re-exec. Confirm the plugin is under `custom_nodes/`, then fully restart the ComfyUI server process. After the restart a **Runflow** category appears in the node picker. Put it under ComfyUI **Settings, Runflow, Connection** (the `Runflow.ApiKey` field). That storage is local to your machine and is never written into a workflow file. The Deploy node also exposes `host` and `api_key` widgets, but ComfyUI persists widget values into the workflow JSON, so a key typed there travels with every export, screenshot, and paste. Leave those widgets empty for normal use. Before you click Deploy on a workflow you did not author, clear both the `host` and `api_key` widgets on the Deploy node (or delete and recreate the node). A workflow from a stranger can pre-fill `host` to send your key to their server. It depends on what you do. Publishing a brand-new endpoint needs `comfyui-workflows:read` plus `comfyui-workflows:create`. Redeploying to the same slug from the same client also needs `comfyui-workflows:edit`. Deleting endpoints needs `comfyui-workflows:delete`. The Deploy button uses `read` to detect an existing slug, then `create` (new) or `edit` (replace). Issue the narrowest set you need: `edit` lets a key replace the graph of any existing endpoint your organization owns, so rotate the key on any suspicion of a leak. Dispatching a run (calling your deployed endpoint) uses the standard `runs:create` scope, the same as any model run, which is separate from the `comfyui-workflows:*` deploy and manage scopes above. [Create a key](https://app.runflow.io/settings/api-keys). There are no version tags published yet; the plugin tracks `main`. If you need a hard guarantee against future renames of node classes, widgets, or settings, pin the commit SHA after you clone. Watch [the releases page](https://github.com/runflow-io/ComfyUI-Runflow/releases) for breaking changes. ## Deploying your workflow The slug comes from the Deploy node's `endpoint_name`: lowercased, whitespace mapped to `-`, and anything outside `a-z`, `0-9`, `-`, `_` stripped. So `Background Removal v2.1` becomes `background-removal-v21` (the `.` is dropped). The default name is `default`, so rename it before deploying or you will publish to a slug literally called `default`. Pick a name without surprises. Renaming `endpoint_name` changes the slug, which creates a new endpoint rather than updating the old one. To replace an existing endpoint, keep the same name and rely on the `comfyui-workflows:edit` scope so the deploy overwrites the same slug in place. The plugin shows the underlying error in a browser alert. The usual causes: - **Alert about an unconfigured key or URL.** `Runflow.ApiKey` is blank in Settings (and the node's `api_key` widget is also blank), or `Runflow.ApiUrl` was cleared. Save the key under Settings and deploy again. - **A `failed` alert citing `401` or an invalid key.** The key is missing, revoked, or for the wrong environment. Reissue it and re-save under Settings. - **`lookup failed` or `403` on redeploy.** Deploy first looks up the existing slug, then replaces it. A key with `create` but no `read` fails at the lookup; a key with `read` but no `edit` fails at the replace. Use `read`, `create`, and `edit` together for iterative deploys. - **`endpoint_name must be URL-safe`.** The slug came out empty after stripping characters. Pick a name with at least one letter or number. Yes. The plugin is the convenient path, but the workflow resource has a REST surface you can call directly with any Runflow key that has the right scopes. Create with a `POST` to the workflows collection, replace the graph or swap GPU mode with a `PATCH`, and soft-delete with a `DELETE`. See [ComfyUI workflows API](/api/comfyui-workflows) for the full endpoint list and request bodies. Delete the workflow with `DELETE /v1/comfyui-workflows/{id}` (needs the `comfyui-workflows:delete` scope). The delete is a soft delete: new runs are rejected with `404` on the dispatch route, but the workflow's historical runs stay reachable through `GET /v1/runs/{id}`. ## Calling your deployed workflow Dispatch a run against your workflow's canonical owner and slug path. `your-org` is your organization slug (shown at the top of the dashboard, derived from your API key) and `your-endpoint` is what `endpoint_name` slugified to: ``` curl -X POST "https://api.runflow.io/v1/comfyui-workflows/your-org/your-endpoint/runs" \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "a sea otter holding a smooth pebble", "width": 1024, "height": 1024 }, "callback_url": "https://your-server.com/webhook" }' ``` The response is intentionally minimal: `{ "run_id": "...", "status": "queued" }`. Track the run by polling `GET /v1/runs/{id}` until its `status_code` reaches a terminal state (`succeeded` or `failed`), or by passing a `callback_url`. See [ComfyUI workflows API](/api/comfyui-workflows) for the request body fields. The `input` object is keyed by the `input_id` of each Runflow Input node in your workflow. The plugin ships typed input nodes: `Runflow Input (String)`, `(Int)`, `(Float)`, `(Boolean)`, `(Image)`, and `(File)`. Set a meaningful `input_id` on each one in your workflow; two inputs of the same type that keep the default id will collide. Send values under those same keys at call time. Use a `Runflow Input (Image)` or `Runflow Input (File)` node, then send its value as either a `runflow://assets/{uuid}` reference to a previously uploaded asset or any `https://` URL (plain `http://` is rejected). A `runflow://` asset used for an *image* input must be a PNG, JPEG, WebP, or GIF; a `Runflow Input (File)` accepts any file type. Runflow does not fetch or size-check an external URL when you submit the run; the worker downloads it at run time, so an unreachable or wrong-type URL surfaces as a run failure rather than a submit error. A completed run's `output` carries `kind: "comfyui_outputs"` and an `outputs` array — one entry per emitted artifact file, each labeled by the `output_id` of the Runflow Output node that produced it. A node that emits several files (a batch, say) yields several entries that share one `output_id`, so treat `output_id` as a node label, not a unique key: ```json { "id": "01J0...", "status_code": "succeeded", "output": { "kind": "comfyui_outputs", "outputs": [ { "output_id": "image", "url": "https://...", "content_type": "image/png", "size_bytes": 1048576 }, { "output_id": "image", "url": "https://...", "content_type": "image/png", "size_bytes": 1041923 } ] } } ``` Iterate the whole array and group by `output_id` when you need per-node outputs. Each entry's `url` is presigned and time-limited; download or copy outputs to your own storage on receipt. If a URL expires before you fetch it, re-request the run with `GET /v1/runs/{id}` and Runflow returns freshly signed URLs. Dispatch is asynchronous. Pass a `callback_url` and Runflow POSTs the terminal run to it once the run reaches a final state, or omit it and poll `GET /v1/runs/{id}` until the status is terminal. See [Runs](/concepts/runs) for the lifecycle and [Callbacks](/concepts/callbacks) plus [Verify callback signatures](/guides/verify-callback-signatures) for the webhook pattern. ## GPUs and billing Deployed workflows bill per second of GPU wall-time: from when a worker picks up your run until it returns a result. There is no fixed per-call price, because the workflow is yours. The charge is applied once per run, to the organization that calls the run (the API key's organization), not the workflow's owner. Idle workers cost nothing, because the platform scales to zero between runs. GPU selection is set on the workflow, not per run, through its `gpu_mode`. In `auto` (the default) the platform routes each run to the cheapest GPU that fits the workflow's memory needs and promotes to a larger one if a run runs out of memory. In `explicit` the workflow is pinned to a chosen set of GPU models. You can switch `gpu_mode` with a `PATCH` to the workflow (owner only); changing the specific set of GPUs an explicit workflow is pinned to is done by redeploying it. The GPU types currently available and their rates are on [runflow.io/pricing](https://www.runflow.io/pricing). Rates are per GPU second and depend on the GPU your run lands on. The authoritative, current rate card lives at [runflow.io/pricing](https://www.runflow.io/pricing); check there before sizing a budget. Total cost for a run is the GPU seconds it used multiplied by that GPU's rate, which is why a faster GPU is not always more expensive overall. To estimate your own workflow, run it once and read the cost and GPU seconds back from `GET /v1/runs/{id}`, then multiply by your expected volume. It depends on where it failed. If a worker picked up your run and then it failed during execution, or the worker was lost after claiming the job, you are billed for the GPU time that was actually consumed, because the platform pays its GPU provider for that time. Failures that happen before any worker starts (input validation, no compatible GPU available, or being rejected at admission for balance or pricing) are not charged. Cancelled runs are not charged. Before dispatch, Runflow checks that your organization has a positive credit balance and that every GPU your run could land on has an active price. A non-positive balance is rejected with `402`. A workflow pinned to a GPU that is inactive or unpriced is rejected with `422`; redeploy it with an available GPU. These checks run before any work starts, so a rejected run is never charged. The platform keeps a deployed workflow's worker warm only while runs are arriving, then spins it down. The next run after an idle window pays a cold start while the worker boots and loads the model weights, and that startup time is part of the billed GPU wall-time. For latency-sensitive work, send a warming run on a schedule or keep the playground open during a session. `GET /v1/runs/{id}` returns the cost and a cost breakdown for completed ComfyUI runs, including the GPU models used and the GPU seconds consumed per model. The webhook delivery payload does not carry cost — it has the run's `status`, `output`, and `duration_ms` — so call `GET /v1/runs/{id}` for the cost and the per-GPU breakdown. To attribute spend per pipeline, set a `metadata` object when you create the run; it is echoed back on both the webhook and the run record. Yes. Runs are bounded by a platform execution-time cap; a run that hits it is stopped. Because billing meters only the GPU time actually consumed, a run stopped at the cap is billed for the GPU seconds it used up to that point. If a workflow legitimately needs a longer ceiling, contact Runflow. ## Limits and behavior A deployed workflow has to be reproducible on the worker, and Runflow enforces that when you run it, not when you deploy it. Deploy itself does not check reproducibility, so a non-reproducible workflow saves and deploys cleanly, then its first run is rejected with `422`. At run time Runflow rejects a custom node that has no pinned commit or whose origin is not `https://` (a short allowlist of Runflow-internal origins aside), and rejects a model with no `sha256` (the worker resolves model weights by that hash from its cache, so a public download URL is not required). ComfyUI itself must report an origin and commit. In practice this means public, pinned dependencies: a private repository the worker cannot clone fails the run. Local working-copy edits are recorded for audit but do not by themselves block a run. The worker does not have your local-only models or custom nodes. Click **Auto setup** locally first to confirm the workflow resolves all of its dependencies (only on workflows you trust, since Auto setup runs `git clone` and `pip install` from URLs declared by the workflow). Models declared as download URLs in the workflow's `properties.models` deploy without a local copy, but each still needs a `sha256` to run: a URL-only entry with no hash is rejected at run time with an `invalid_sha` error. Widget-selected models must exist locally or carry a URL fallback, and likewise need a `sha256`. Yes. Redeploy replaces the workflow in place under the same slug. Each run snapshots the workflow at the moment it is created, and that snapshot is what executes, so a run already in flight is unaffected by a redeploy that lands while it is running. Workflows are private to your organization by default (`is_public` is `false` on create). A private workflow's graph and endpoint are not visible or runnable by other organizations. Making a workflow public lets anyone who has its exact owner and slug load and run it by direct link, but it does not appear in Runflow's browseable catalog unless the Runflow team approves it for listing. Either way, callers still need their own beta access and credits to run it. Yes. The API applies a request-rate limit; if you exceed it, calls return `429` with the error code `RATE_LIMITED`. Retry with backoff (exponential backoff is a safe default). Separately, when all suitable GPU workers are busy, a dispatched run waits in `queued` until one frees up rather than being rejected. See [Rate limits](/concepts/rate-limits) for more, and use the contact link there if you need a higher limit. Not in the current version. There is no customer-facing way to cancel an in-progress ComfyUI run today; runs end on their own when they complete, fail, or hit the maximum duration. ComfyUI ships with no authentication, and custom nodes can run arbitrary code on the host, so a ComfyUI port reachable from the public internet is a real risk. The Runflow settings panel has two controls for this: **password authentication** and a **port exposure scan** that reports your listen binding and checks whether your ports are reachable from the internet. Password auth takes two steps: set a username and password, *and* turn on the **Enable password authentication** toggle, which is off by default. Only with the toggle on and both fields set does the plugin require HTTP Basic auth on every request; a username and password alone leave the server open. If a port is open, enable password auth (toggle included), put ComfyUI behind a firewall or an authenticated proxy, or bind to localhost only. The port exposure scan sends your public IP address to a third-party service (portchecker.io) so it can probe your ports from outside. No workflow data is sent. ## Getting help The main place to reach us is the [Runflow Discord community](https://discord.gg/Vm6ardjC7T). For bugs in the ComfyUI-Runflow plugin (the node package) you can also open an issue on [GitHub](https://github.com/runflow-io/ComfyUI-Runflow/issues), and for account or billing questions you can [talk to the team](https://cal.com/team/runflow/talk-to-founders). Including your organization slug, the endpoint slug, and a run id (when a specific run is involved) helps us respond faster. ## Related The full install, configure, deploy, and call walkthrough. The REST surface: dispatch, manage, inputs and outputs. Run lifecycle, polling, and callbacks. The current GPU rate card. --- # Deploy a ComfyUI workflow **Beta.** ComfyUI Deploy is in public beta. The plugin, API surface, and pricing rate cards are stable but may evolve. Expect breaking changes to be called out in [the changelog](https://github.com/runflow-io/ComfyUI-Runflow/releases) before they ship. [`ComfyUI-Runflow`](https://github.com/runflow-io/ComfyUI-Runflow) is a custom-node plugin that publishes a ComfyUI workflow to Runflow as a callable API. The plugin captures the workflow graph, the runtime manifest (ComfyUI commit, custom-node commits, package versions, cached models), and uploads it on a single click. The deployed workflow gets a playground in the Runflow dashboard and a stable API surface invocable with any Runflow key that has the right scopes. Two things to internalize before you click Deploy on anything you did not author: 1. **Never type your API key into the Deploy node's `api_key` widget.** ComfyUI persists widget values into the workflow JSON, so a key entered on the node travels with every export, screenshot, paste, and bug-report attachment. Configure the key under **Settings → Runflow → Connection** instead; that storage is local to your machine and never serialized into a workflow file. The same caution applies to the Deploy node's `host` widget. A workflow you load from a stranger can pre-populate `host` to send your key to their server when you click Deploy. **Before clicking Deploy on any imported workflow, clear both `host` and `api_key` on the Deploy node** (or delete and recreate the node so the widgets reset to empty). 2. **Auto setup runs `git clone` and `pip install` from URLs declared by the loaded workflow.** Treat it like any other untrusted shell script: only click Auto setup for workflows from sources you trust. A hostile workflow can use the button to execute arbitrary code on your machine. ## What you'll do Three steps: install the plugin, configure your key under Settings, click Deploy. The optional Runflow Input / Output nodes give the deployed API a clean schema. ## Video walkthrough End-to-end walkthrough of everything below (install, configure, deploy, call): ## Prerequisites - A working ComfyUI install. - A Runflow API key. [Create one](https://app.runflow.io/settings/api-keys) with the scopes below. - `python`, `pip`, and `git` on `PATH` (used by the optional Auto setup feature). ### Key scopes, by what you actually do | Doing | Scopes | |---|---| | Publish a brand-new endpoint, never update from this client | `comfyui-workflows:read` + `comfyui-workflows:create` | | Iterate (redeploy to the same slug from this client) | add `comfyui-workflows:edit` | | Remove workflows from this client | add `comfyui-workflows:delete` | The Deploy button uses `read` to detect existing slugs and either `create` (new) or `edit` (replace) accordingly. `edit` is broad: it lets the key replace the graph of any existing endpoint owned by the org without changing the slug. If the key leaks, an attacker holding `edit` can silently swap a production workflow's graph. Issue the narrowest scope set you actually need and rotate on any suspicion of leak. The plugin sends `Authorization: Bearer ` and lets the API derive your organization from the key. No organization id is needed. ## Step 1: install the plugin Clone the plugin into your ComfyUI `custom_nodes/` directory and restart ComfyUI. `$COMFYUI_DIR` below is your ComfyUI install root (the folder that contains `main.py`, `custom_nodes/`, `models/`, etc.): ```bash cd "$COMFYUI_DIR/custom_nodes" git clone https://github.com/runflow-io/ComfyUI-Runflow.git # or, for development against a checkout you already have: # ln -s /path/to/ComfyUI-Runflow ComfyUI-Runflow ``` Restart ComfyUI (a UI refresh is not enough; the Python process has to re-exec). After the restart you'll see a new **Runflow** category in the node picker. The plugin is at `1.0.0` on `main` HEAD; no version tags are published yet. Pin the commit SHA after `clone` if you need a hard guarantee against future renames of node classes, widgets, or settings. ## Step 2: configure your account Open ComfyUI **Settings → Runflow → Connection** and fill in: | Setting | Default | What it is | |---|---|---| | `Runflow.ApiUrl` | `https://api.runflow.io` | API base URL. Override only when running against a staging stack. | | `Runflow.ApiKey` | _(empty)_ | The Runflow API key from above. | Settings persist locally on your machine; they are not written into any workflow file. **Do this once and forget the per-node widgets exist.** The Runflow tab holds more than these connection fields; see [The Runflow settings panel](#the-runflow-settings-panel) for the full reference, including the security controls. The Deploy node also exposes `host` and `api_key` widgets, but, as called out at the top of this page, those values are persisted into the workflow JSON and travel with every export. The widgets are there for ephemeral multi-account testing on a machine you control; for normal use, leave them empty and rely on the global Settings. ## Step 3: deploy Add a **Runflow Deploy** node (under the **Runflow** category) and set its `endpoint_name` widget. The default is `default`; rename it before clicking Deploy or you'll publish to a slug literally called `default`. The slug is derived from this name: lowercased, with whitespace mapped to `-`, and anything outside `a-z`, `0-9`, `-`, `_` stripped. So `Background Removal v2.1` becomes `background-removal-v21` (the `.` is silently dropped); pick a name without surprises. The Runflow Deploy node in ComfyUI with endpoint_name, host, and api_key widgets, and Deploy and Auto setup buttons below them. Click **Deploy**. The button flashes **Deployed ✓** on success. On failure the plugin shows a browser `alert()` with the underlying error (missing API key, missing scopes, slug validation, HTTP failure, etc.) and the button returns to **Deploy**. The HTTP response is the terminal state; there is no separate deployment job to poll. On success, open the workflow in [`app.runflow.io`](https://app.runflow.io), test it in the playground, inspect the graph the platform recorded, and adjust any settings exposed by your Runflow Input nodes. ## Call the deployed workflow Dispatch a run against your workflow's canonical `your-org/your-endpoint` path, where `your-org` is your organization slug (visible at the top of the dashboard, derived from the API key) and `your-endpoint` is what `endpoint_name` slugified to. The request body uses the same shape as a model run. The example below assumes a workflow with three Runflow Input nodes (`input_id` set to `prompt`, `width`, `height`); replace those keys with whatever `input_id` you set on your own input nodes. ``` curl -X POST https://api.runflow.io/v1/comfyui-workflows/your-org/your-endpoint/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "a sea otter holding a smooth pebble, studio photography", "width": 1024, "height": 1024 }, "callback_url": "https://your-server.com/webhook" }' ``` Omit `callback_url` to poll [`GET /v1/runs/{id}`](/concepts/runs) instead. See [Runs](/concepts/runs) for the lifecycle, [Callbacks](/concepts/callbacks) + [Verify callback signatures](/guides/verify-callback-signatures) for the async pattern, and the [JavaScript SDK guide](/guides/javascript-sdk) for a typed client. Invocations bill against the calling key like any other model run. ### What you get back The completed run's `output` carries `kind: "comfyui_outputs"` and an `outputs` array — one entry per emitted artifact file, each labeled by the `output_id` of the Runflow Output node that produced it. A node that emits several files (a batch) yields several entries sharing one `output_id`: ```json { "id": "01J0...", "status_code": "succeeded", "output": { "kind": "comfyui_outputs", "outputs": [ { "output_id": "image", "url": "https://...", "content_type": "image/png", "size_bytes": 1048576 }, { "output_id": "image", "url": "https://...", "content_type": "image/png", "size_bytes": 1041923 } ] } } ``` Each entry's `url` is presigned and time-limited. Iterate the whole array — group by `output_id` when you need per-node outputs — and download or re-upload outputs to your own storage on receipt. ## Billing & GPU pricing Deployed ComfyUI workflows bill per **second of GPU wall-time**: from the moment the worker boots the container for your run to the moment it returns the result. Idle workers cost nothing (the platform scales to zero between runs); cold starts on a new GPU type are billed alongside the run. ### What you pay per second Rates are per GPU second and depend on which GPU your run lands on. The current authoritative rate card lives at [runflow.io/pricing](https://www.runflow.io/pricing); check there before sizing a budget. Rates apply uniformly across runs from any caller (your API key, the playground, embedded Studio). To estimate a specific workflow, run it once and read the cost and GPU seconds back from the run record (see [What the run record shows](#what-the-run-record-shows)). ### Picking a GPU GPU selection is set on the **workflow**, not per run. Each ComfyUI workflow carries a `gpu_mode` of either: - **`auto`** (default): the platform routes each run to the cheapest GPU that fits the workflow's memory footprint. Use this unless you have a specific reason. - **`explicit`**: the workflow is pinned to a chosen set of GPU models in `gpu_explicit_models[]`. Use this when a workflow needs guaranteed VRAM or a known fast path. Switch `gpu_mode` via `PATCH /v1/comfyui-workflows/{id}` (workflow owner only); changing the specific GPU set an explicit workflow is pinned to is done by redeploying. Once a workflow is explicit, every run uses that GPU set regardless of caller. ### Scale-to-zero behavior The platform keeps a deployed workflow's worker warm only while runs are arriving. After a short idle window the worker spins down and the next run pays a cold-start cost (typically 10-40s of GPU time depending on the model weights). For latency-sensitive workloads, send a warming run on a schedule, or keep the playground open during a session. ### What the run record shows `GET /v1/runs/{id}` returns billing metrics for completed ComfyUI runs (GPU model used and GPU seconds consumed). The webhook delivery payload does not carry them — it has the run's `status`, `output`, and `duration_ms` — so read cost and GPU seconds from `GET /v1/runs/{id}`. To attribute spend per pipeline, set a `metadata` object at run creation; it is echoed back on both the webhook and the run record. ## Define the external schema with Runflow Input / Output nodes The plugin ships typed input and output nodes that declare the deployed API surface. Wire them into your workflow; everything else stays an internal implementation detail. A ComfyUI workflow with three Runflow Input nodes (prompt, width, height) feeding a model node, with a Runflow Output (IMAGE) and the Runflow Deploy node also wired in. The node classes available today, grouped by category in the node picker: | Node | Class | Category | Default `input_id` / `output_id` | Purpose | |---|---|---|---|---| | `Runflow Input (String)` | `RunflowInputString` | `Runflow/Input` | `string_input` | String field on the deployed API. | | `Runflow Input (Int)` | `RunflowInputInt` | `Runflow/Input` | `int_input` | Integer field. | | `Runflow Input (Float)` | `RunflowInputFloat` | `Runflow/Input` | `float_input` | Floating-point field. | | `Runflow Input (Boolean)` | `RunflowInputBoolean` | `Runflow/Input` | `boolean_input` | Boolean field. | | `Runflow Input (Image)` | `RunflowInputImage` | `Runflow/Input` | `image_input` | Image input; callers send a URL or upload an asset. | | `Runflow Input (File)` | `RunflowInputFile` | `Runflow/Input` | `file_input` | File input (a filename string in the graph); callers send a URL or upload an asset of any type. | | `Runflow Output (Image)` | `RunflowOutputImage` | `Runflow/Output` | `image_output` | Names an IMAGE output. Each image in the batch is saved as PNG and returned. | | `Runflow Output (File)` | `RunflowOutputFile` | `Runflow/Output` | `file_output` | Names a file output (video, 3D mesh, audio, archive) already written under ComfyUI's `output/` directory by an upstream save node. | Each input has three widgets: `input_id` (the stable join key the deployed API uses; never mutated at deploy time), `display_name` (label shown in the playground), `description` (help text). Each output has `output_id` and `output_name`; `Runflow Output (File)` additionally takes the file path on its forced `value` socket. **Change `input_id` and `output_id` to something meaningful in your workflow**; two inputs of the same type with the default ids will collide. Locally these nodes are pure pass-throughs of the `value` socket. At deploy time, the Runflow inference service rewrites each input's upstream to inject the caller-supplied value and maps each output node id to its `output_id` so the callable API returns a stable, named set of artifacts. ### Encoder bridges On a recent ComfyUI (one exposing the `comfy_api.latest` module), the plugin also registers a **`Runflow/Save`** family. Each one encodes a native ComfyUI socket to a file under `output/` and emits the relative filename on a STRING socket, ready to wire straight into a `Runflow Output (File)` node. On older installs these nodes are skipped at load with a single warning, and the rest of the plugin still works. | Node | Input | Format | |---|---|---| | `Runflow Save Audio (FLAC)` | AUDIO | FLAC, lossless. | | `Runflow Save Audio (MP3)` | AUDIO | MP3, with a `quality` widget (`V0`, `128k`, `320k`). | | `Runflow Save Audio (Opus)` | AUDIO | Opus, with a bitrate widget (`64k` to `320k`). | | `Runflow Save Video (MP4)` | VIDEO | MP4 / H.264. | | `Runflow Save Video (WEBM)` | IMAGE batch + `fps` | WebM / VP9 or AV1. | ## Auto setup Directly below Deploy on the same node, the **Auto setup** button installs every custom node and downloads every model the active workflow references. Useful when you load someone else's workflow and want to skip chasing dependencies by hand. It is also the worst possible button to click on a workflow you do not trust (see the warning at the top of the page). Clicking the button opens a modal with two checkboxes (*Install missing custom nodes*, *Download missing models*) and a Start button. The Auto setup modal with 'Install missing custom nodes' and 'Download missing models' checkboxes both ticked, and Cancel and Start buttons. While the job runs, the modal shows a byte-progress bar for the current model download and a count-progress bar for custom-node installs; models and nodes install in parallel. When the job finishes, click **Restart ComfyUI** in the modal so the newly installed nodes register. The Auto setup modal mid-run, reporting '0 missing models and 1 missing custom node', an 'Installing custom nodes 0 of 1' progress bar, and a scrolling log of pip install output. Auto setup uses `git clone --depth=1` plus `git fetch --depth=1 origin ` plus `git checkout ` (with a full-clone fallback if the host disables SHA-targeted fetches), then `python -m pip install -r requirements.txt` if the cloned repo carries one. Works on Linux, macOS, and Windows. Workflows can also declare model-download URLs in `properties.models[].url`. The deploy worker fetches those server-side before running the workflow. A hostile URL there is a server-side problem the platform mitigates, but **review the URLs before deploying a community workflow**; the dashboard surfaces the recorded graph after deploy if you want to double-check. ## The Runflow settings panel Everything the plugin stores on your machine lives under ComfyUI **Settings → Runflow**, split into two groups: **Connection** and **Authentication**. None of these values are ever written into a workflow file. ComfyUI Settings with the Runflow tab selected, showing an Authentication section with a 'Run security scan' button, an 'Enable password authentication' toggle, Username and Password fields, and a Connection section with API Key and API URL fields. ### Connection The two fields the Deploy button reads, both covered in [Step 2](#step-2-configure-your-account): | Setting | Default | What it is | |---|---|---| | `API Key` (`Runflow.ApiKey`) | _(empty)_ | The `rf_live_*` key used when the Deploy node's `api_key` widget is empty. | | `API URL` (`Runflow.ApiUrl`) | `https://api.runflow.io` | API base URL. Override only when running against a staging stack. | ### Authentication ComfyUI ships **no authentication** by default, and custom nodes can run arbitrary code on the host. A ComfyUI port reachable from the public internet is therefore a serious risk: anyone who finds it can execute code on your machine. This group helps you detect and close that exposure. | Setting | Default | What it is | |---|---|---| | `Enable password authentication` | _off_ | Require HTTP Basic auth on **every** request to this ComfyUI server. Takes effect only once both a username and password are set. | | `Username` / `Password` | _(empty)_ | Credentials checked by the auth layer, stored in `runflow_security.json` next to your ComfyUI install. | #### Port exposure scan Click **Run security scan** to check whether this machine is reachable from outside your network: 1. It reports ComfyUI's **listen binding**, read locally from its `--listen` / `--port` args. Binding to `0.0.0.0` / `::` (all interfaces) is flagged in red; `127.0.0.1` (local only) is green. 2. It looks up your **public IP**, then asks the third-party service **portchecker.io** whether your ComfyUI port (plus `80`, `8080`, `443`) is reachable from the internet, showing each port as OPEN or closed. Port exposure scan results reading 'Listening on port 8188, bound to 127.0.0.1 - local only', a table of ports 80, 443, 8080, and 8188 all marked closed, and a green banner: 'None of the scanned ports are reachable from the public internet.' The scan sends your public IP address to portchecker.io so it can probe you from the outside. No workflow data is sent. If a port shows OPEN (or you are bound to all interfaces), close the exposure: enable password authentication above, put ComfyUI behind a firewall or an authenticated reverse proxy, or bind to localhost only (start ComfyUI without `--listen`, or with `--listen 127.0.0.1`). ## Troubleshooting - **No `Runflow` category in the node picker.** ComfyUI loaded before the plugin was on disk, the plugin landed in the wrong `custom_nodes/` directory, or you only refreshed the browser tab. Confirm the path, then fully restart the ComfyUI server process. - **Clicking Deploy raises an alert about an unconfigured key or URL.** Either `Runflow.ApiKey` is blank in Settings (and the node's `api_key` widget is also blank), or `Runflow.ApiUrl` was cleared. Save the key under Settings and click Deploy again. - **`Deploy` returns `401 Unauthorized`.** The key is missing or revoked. Reissue and re-save under Settings. - **`Deploy` raises a `lookup failed` or `403 Forbidden` alert on redeploy.** Deploy first GETs the existing slug, then PATCHes it. A key with `comfyui-workflows:create` but no `:read` fails at the lookup with `lookup failed`; a key with `:read` but no `:edit` fails at the PATCH with `403`. Reissue with all three of `read`, `create`, `edit` for iterative deploys. - **`Deploy` alerts that `endpoint_name` must be URL-safe.** The slug came out empty after stripping characters outside `a-z`, `0-9`, `-`, `_`. Pick a name with at least one allowed character. - **A workflow that runs locally fails on Runflow.** Local-only models or custom nodes are not on the worker. Click **Auto setup** locally first to confirm the workflow resolves (only on workflows you trust). Models declared via `properties.models` URLs deploy without needing a local copy; widget-selected models must exist locally or carry a URL fallback. - **The endpoint slug changed unexpectedly.** Renaming `endpoint_name` creates a new slug. To replace an existing endpoint, keep the same name and rely on `comfyui-workflows:edit`. ## Related Decision tree from task to model. Lifecycle, polling, callbacks. HMAC verification for the callback you set above. Typed client for the deployed API. --- # Embed the Studio [`@runflow-io/studio`](https://www.npmjs.com/package/@runflow-io/studio) ships the production Runflow Studio UI as a mountable component. Drop it into a `
`, point it at a proxy you control, and end users get the full workflow surface without leaving your site. ## What you'll do End-to-end embed in two files: a server route that runs the proxy, and a browser entry that mounts the Studio. ## Prerequisites - A Runflow API key. [Create one.](https://app.runflow.io/settings/api-keys) - A server framework that speaks Web Standards `Request`/`Response` (Next.js App Router, Hono, Workers, SvelteKit, Bun, Deno), or use the Node adapter for Express/Fastify. - React 18 or 19 in the host page (or use the npm package, which lists React as a peer dependency). ## Install For the bundled embed (steps 1 and 2 below), you only need the Studio and the proxy: ```bash bun add @runflow-io/studio @runflow-io/proxy ``` Add `@runflow-io/sdk` if you also use [headless mode](#headless-mode). ## Step 1: mount the proxy The Studio talks to `api.runflow.io` through your server so your API key never reaches the browser bundle. The proxy is a Web Standards handler. ```ts // app/api/runflow/[...path]/route.ts (Next.js App Router) import { runflowProxy } from "@runflow-io/proxy"; export const { GET, POST } = runflowProxy({ apiKey: process.env.RUNFLOW_API_KEY!, authenticate: async (req) => { const session = await getSession(req); if (!session) return null; return { userId: session.userId }; }, }); ``` Without an `authenticate` hook, the proxy is reachable by anyone with the URL. The CSRF defaults block browser drive-bys but not direct server-to-server callers (curl, scripts, other backends) — they let your Runflow budget burn against any allowed model. Full proxy options, including `rateLimit`, `allowedModels`, and `onRun`, are in [the SDK guide](/guides/javascript-sdk#auth-rate-limit-telemetry-hooks). ## Step 2: mount the Studio ```ts import { mount } from "@runflow-io/studio"; const studio = mount("#studio", { urls: { runflowProxy: "/api/runflow" }, theme: "auto", }); // later, to tear it down studio.unmount(); ``` ```html
``` The Studio injects its own stylesheet into `` on mount. Pass `injectStyles: false` if you ship your own CSS. ## Mount options ```ts mount(target: string | HTMLElement, options?: { urls?: { runflowProxy?: string; // /api/runflow dispatch + poll runflowDevProxy?: string; // unreleased models (off by default) imageProxy?: string; // /api/runflow/image same-origin image fetch upload?: string; // /api/runflow/upload multipart to public URL chat?: string; // /api/runflow/chat chat agent (SSE) }; theme?: "light" | "dark" | "auto" | ThemeOverrides; injectStyles?: boolean; }): { unmount(): void }; ``` If you only need single-step dispatch (no uploads, no chat), `runflowProxy` alone is enough. The upload-dependent and chat-dependent paths in the UI degrade gracefully when the routes return 404 or 403. ## Companion endpoints The Studio dispatches against multiple endpoints. `@runflow-io/proxy` handles the Runflow API paths; the others are customer-provided because they touch your storage and your AI provider keys. | URL key | What lives there | Provided by | Security must-haves | |---|---|---|---| | `runflowProxy` | `POST /v1/models/{model}/runs`, `GET /v1/runs/{id}`, `GET /v1/health` | `@runflow-io/proxy` | Add `authenticate`, see [hooks](/guides/javascript-sdk#auth-rate-limit-telemetry-hooks). | | `imageProxy` | `GET /?url=` returns the bytes same-origin | You | Validate the URL host against a CDN allowlist and reject RFC1918 / `169.254.0.0/16` / `localhost` to prevent SSRF (CWE-918). | | `upload` | `POST` multipart returns `{ url }` (writes to your storage) | You | Enforce content-type and size limits before writing; the Studio does not sanitize uploads. | | `chat` | SSE chat agent (wraps your AI provider plus the tool catalogue) | You | Gate behind your existing user auth; your AI provider key is on the line. | The `StudioUrls` type also includes additional optional keys for internal Runflow paths (an evaluation endpoint, a dev-only proxy for unreleased models). They default off and most embeds do not configure them. Check `packages/studio/src/lib/urls.ts` in [`runflow-js`](https://github.com/runflow-io/runflow-js) for the live shape. ## Theming The Studio uses CSS custom properties under the `--rfs-*` prefix. Pass `theme: "light" | "dark" | "auto"` for the bundled defaults, or override specific tokens: ```ts mount("#studio", { theme: { accent: "#FBBF24", bg0: "#0a0a0b", ink0: "#fafafa", }, }); ``` All theme values become `--rfs-*` custom properties on the mount root, so you can also override them from your own CSS. ## Workflows that ship `BUILTIN_TOOLS` ships 13 single-step workflows: `ai-edit`, `ai-scene`, `reference-inpaint`, `product-isolation`, `smart-resize`, `outpaint`, `background-color`, `background-removal`, `tag-removal`, `object-removal`, `model-removal`, `skin-fix`, `topaz-upscale`. Each maps to a model in the [catalog](/models). Iterate `BUILTIN_TOOLS` from the headless entry for the live set. ## Headless mode To skip the bundled UI and build your own with the same tool catalogue: ```ts import { findTool } from "@runflow-io/studio/headless"; import { Runflow } from "@runflow-io/sdk"; const rf = new Runflow({ baseUrl: "/api/runflow" }); const tool = findTool("background-removal"); if (!tool) throw new Error("Tool 'background-removal' not in BUILTIN_TOOLS"); const { output } = await rf.tools.run(tool, { image: "https://example.com/photo.jpg", }); ``` The headless entry also exports `BUILTIN_TOOLS`, `WORKFLOWS`, `SAMPLES`, the workflow dispatcher, and `setStudioUrls`. The `WORKFLOWS` surface is pre-1.0 and may change between patch releases. ## Related Server-side, proxy, and the tool DSL. Workflows backed by real models. Bearer header. Decision tree. --- # Handle async callbacks ## What you'll do Stand up a webhook handler that takes a Runflow callback, persists the result, and returns `200` fast. ## Prerequisites - A Runflow API key. [Create one.](https://app.runflow.io/settings/api-keys) - A public URL for the handler. Use [ngrok](https://ngrok.com) for local dev. - A database or queue to persist results. ## Steps The handler should: parse the body, persist the relevant fields, return `200` within a few seconds. These examples parse JSON directly for a local prototype. In production, verify `Runflow-Signature` against the raw request body before parsing JSON; see [Verify callback signatures](/guides/verify-callback-signatures). ```javascript Express import express from "express"; const app = express(); app.use(express.json({ limit: "5mb" })); app.post("/webhook/runflow", async (req, res) => { const { event, run_id, status, output } = req.body; // Persist immediately. Output URLs are presigned; copy them to your storage. await db.runs.upsert({ run_id, event, status, output }); res.sendStatus(200); // Heavy work (downloading outputs) goes in a background job. }); app.listen(3000); ``` ```python FastAPI from fastapi import FastAPI, Request app = FastAPI() @app.post("/webhook/runflow") async def runflow_webhook(req: Request): body = await req.json() await db.runs.upsert( run_id=body["run_id"], event=body["event"], status=body["status"], output=body.get("output"), ) return {"ok": True} ``` ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "image_url": "https://...", "prompt": "..." }, "callback_url": "https://your-server.com/webhook/runflow" }' ``` Runflow retries failed callbacks. Match on `run_id` for run callbacks or `batch_id` for batch callbacks, and skip if you have already processed it. ```javascript const existing = await db.runs.findByRunId(run_id); if (existing?.status === "succeeded") return res.sendStatus(200); ``` Output URLs are presigned and time-limited. Download or re-upload them to your own bucket on receipt. Do not store the raw URLs as your source of truth. ## Verify it worked Trigger a run, watch your handler logs: ``` POST /webhook/runflow 200 1234ms { event: 'run.completed', run_id: '01J0...', status: 'succeeded' } ``` You have a row in your DB with `status: succeeded`. You're done. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Callback arrives but body is empty | Missing body parser | For unsigned prototypes, use `express.json()` or framework equivalent. For signed handlers, read the raw body first. | | Handler times out | Heavy work in the request | Return `200` first, defer heavy work. | | Same payload arrives twice | Retry on a 5xx your handler returned | Be idempotent on `run_id`. | | Nothing arrives | URL not public | Use ngrok or Cloudflare Tunnel for local dev. | ## Production hardening - [Verify the signature](/guides/verify-callback-signatures) so spoofed POSTs cannot reach your DB. - Wrap your handler in a queue (SQS, Pub/Sub, BullMQ) so a slow downstream cannot drop callbacks. - Monitor `4xx` and `5xx` from your endpoint. Runflow stops retrying after a few attempts. ## Related HMAC verify in code. Pattern, retries, redelivery. --- # JavaScript SDK [`@runflow-io/sdk`](https://www.npmjs.com/package/@runflow-io/sdk) wraps the Runflow REST API in a typed client and a small tool DSL. It uses Web Standards `fetch`, so the same package runs in Node, Bun, Deno, browsers, and edge workers. All `@runflow-io/*` packages are pre-1.0 (currently `0.0.3`). Patch releases may change types or runtime behavior. Pin exact versions in production until 1.0. ## Install ```bash bun add @runflow-io/sdk # or: npm install @runflow-io/sdk # or: pnpm add @runflow-io/sdk ``` The SDK has zero runtime dependencies. Node 18+ or any environment with a global `fetch`. ## Server-side: dispatch and wait ```ts import { Runflow } from "@runflow-io/sdk"; const rf = new Runflow({ apiKey: process.env.RUNFLOW_API_KEY }); const dispatched = await rf.models.run("runflow/background-removal", { input: { image_url: "https://example.com/photo.jpg" }, }); const final = await rf.runs.wait(dispatched.id, { timeoutMs: 120_000, pollIntervalMs: 1500, onPoll: (run) => console.log(run.status_code), }); console.log(final.output); ``` `rf.models.run(model, body)` dispatches and returns immediately with `{ id, status_code, ... }`. `rf.runs.wait(id)` polls until the run finishes and resolves with the final record. `onPoll` fires on each poll if you want progress updates. Model ids are `owner/slug` (or `owner/slug/sub`). A bare slug like `"background-removal"` returns `HTTP 405` from the API directly and `HTTP 403 Not allowed` through the proxy. The SDK rejects empty model ids and any segment equal to `.` or `..` at the client with `code: invalid_model_id`, and URL-encodes every other segment, so it is safe to pass user or LLM-controlled model ids as long as you pair them with an `allowedModels` policy on the proxy. Defaults for `runs.wait`: `timeoutMs: 180_000`, `pollIntervalMs: 2_000`. If the wait times out, the run is still going upstream; you can extend the deadline or use `rf.runs.get(err.runId)` to check status separately. To stream every poll yourself, use `rf.runs.poll(id)`, which returns an async iterable. It yields each successful response, retries 5xx silently between yields, and throws `RunTimeoutError` if the run has not finished within `timeoutMs`. ```ts for await (const run of rf.runs.poll(dispatched.id)) { console.log(run.status_code); } ``` ## The Tools DSL `defineTool` binds a model id to a typed input schema, an output schema, and a `buildRequest` function that maps inputs to the model's body shape. Once defined, the same object can be dispatched from the SDK or rendered by the Studio. ```ts import { defineTool, imageInput, textInput, imageOutput, Runflow, } from "@runflow-io/sdk"; const sceneSwap = defineTool({ id: "ai-scene", name: "Drop into a new scene", model: "google/nano-banana-pro/edit", inputs: { image: imageInput({ source: "runtime" }), prompt: textInput({ source: "user", label: "Describe the scene", maxLength: 400, }), style: textInput({ source: "preset", value: "Photoreal product photography, true colors preserved", }), }, output: { image: imageOutput() }, buildRequest: ({ image, prompt, style }) => ({ input: { prompt: `Place the subject ${prompt}. ${style}.`, image_urls: [image], }, }), }); const rf = new Runflow({ apiKey: process.env.RUNFLOW_API_KEY }); const { output } = await rf.tools.run(sceneSwap, { image: "https://example.com/sneaker.png", prompt: "on a windswept rooftop at golden hour", }); console.log(output.image); ``` ### Input sources Each input has a `source` that controls where its value comes from: | Source | Collected from caller? | Passed to `buildRequest`? | |---|---|---| | `preset` | No, baked into the tool's `value` field. | Yes. | | `runtime` | Yes, every call (for example, the source image). | Yes. | | `user` | Yes, from the Studio UI or programmatically. | Yes. | `buildRequest` receives every input including presets (the `AllInputValues` type). Callers only supply non-preset inputs (the `RuntimeInputValues` type), so the destructured `style` in the example above works even though `style` is a preset. Optional `user` inputs use `optional: true` and become optional in `RuntimeInputValues` too. ### Input and output builders Inputs: `imageInput`, `textInput`, `numberInput`, `colorInput`, `selectInput`, `referenceInput`, `maskInput`, `pinInput`. Outputs: `imageOutput`, `textOutput`, `numberOutput`, `jsonOutput`, `imageListOutput`. The default extractor only fills the `image` field. If you declare any other output field without supplying `extractOutput`, that field will be `undefined` at runtime even though the TypeScript types claim it exists. Always supply `extractOutput` for non-image-only schemas. ```ts const upscale = defineTool({ id: "upscale", name: "Upscale", model: "topaz/upscale/image", inputs: { image: imageInput({ source: "runtime" }) }, output: { image: imageOutput(), width: numberOutput(), height: numberOutput() }, buildRequest: ({ image }) => ({ input: { image_url: image } }), extractOutput: (raw) => { const url = (raw as { image_urls: string[] }).image_urls[0]; return { image: url, width: 4096, height: 4096 }; }, }); ``` ### Dispatch without waiting `rf.tools.dispatch(tool, args)` returns `{ runId, model }` immediately if you want to manage polling yourself or relay the id to a callback handler. ```ts const { runId, model } = await rf.tools.dispatch(sceneSwap, { image, prompt }); // later, from a webhook handler or a polling worker const final = await rf.runs.wait(runId); ``` Note the asymmetry: `rf.models.run(...)` returns `{ id, status_code, ... }` (the field is `id`), while `rf.tools.dispatch(...)` returns `{ runId, model }`. Both ids are interchangeable when passed to `rf.runs.get` / `rf.runs.wait` / `rf.runs.poll`. ## Browser, through a proxy Never put a Runflow API key in a browser bundle. Mount [`@runflow-io/proxy`](https://www.npmjs.com/package/@runflow-io/proxy) on your server, point the SDK at it, and the key stays server-side. ```ts // browser const rf = new Runflow({ baseUrl: "/api/runflow" }); ``` When `baseUrl` is set the SDK omits the `Authorization` header. The proxy injects it before forwarding upstream. See [Proxy browser calls server-side](#proxy-browser-calls-server-side) below. ## Configuration ```ts interface RunflowConfig { apiKey?: string; // server-side; sent as Authorization: Bearer baseUrl?: string; // browser; usually "/api/runflow" apiBase?: string; // override upstream base; default https://api.runflow.io requestTimeoutMs?: number; // per-request timeout; default 30_000 ms headers?: Record; fetch?: typeof fetch; // custom fetch (useful in tests) } ``` Exactly one of `apiKey` or `baseUrl` is required. If both are set, `baseUrl` wins and the bearer header is omitted. API keys are alphanumeric plus underscore (current format: `rf_live_*` for inference, `rf_svc_*` for admin). The constructor rejects keys with hyphens, dots, or other punctuation with `code: invalid_api_key`. Strip whitespace before passing. ## Errors The SDK throws three error classes, all extending `RunflowError`: | Error | When it throws | |---|---| | `RunflowError` | HTTP failure, network error, request timeout, bad config, invalid model id, invalid run id, malformed apiKey. | | `RunFailedError` | A run finished with `status_code: failed` or `canceled`. Thrown by `runs.wait`, `runs.poll`, and `tools.run`. Has `.run.id`, `.run.status`, `.run.error`. | | `RunTimeoutError` | `runs.wait` / `runs.poll` did not see a terminal status before `timeoutMs`. Has `.runId`, `.elapsedMs`. | Common `RunflowError.status` and `code` values: | Trigger | `err.status` | `err.code` | |---|---|---| | Missing/invalid API key | 401 | (server-set) | | Proxy denied (model not in allowlist, origin mismatch, path not allowed) | 403 | (server-set) | | Payload too large (proxy 32 KB cap by default) | 413 | (server-set) | | Content-Type not JSON on proxy | 415 | (server-set) | | Rate limit (check `Retry-After` header) | 429 | (server-set) | | Upstream timed out via proxy | 504 | (server-set) | | Per-request timeout (default 30 s) | undefined | `request_timeout` | | Network error | undefined | `network_error` | | Empty model id or `.`/`..` segment | undefined | `invalid_model_id` | | Run id contains `/`, `.`, or `..` | undefined | `invalid_run_id` | | Malformed apiKey at construction | undefined | `invalid_api_key` | | Default extractor found no image URL | undefined | `output_parse_error` | ```ts import { RunFailedError, RunTimeoutError, RunflowError } from "@runflow-io/sdk"; try { const run = await rf.runs.wait(id); } catch (err) { if (err instanceof RunFailedError) { // Log err.run.error server-side; do not surface upstream messages to end users. console.error("run failed:", err.run.error); } else if (err instanceof RunTimeoutError) { console.error("run timed out:", err.runId); } else if (err instanceof RunflowError) { console.error("HTTP", err.status, err.code, err.message); } else { throw err; } } ``` ## Proxy browser calls server-side [`@runflow-io/proxy`](https://www.npmjs.com/package/@runflow-io/proxy) is a Web Standards request handler. It accepts only the run-dispatch and run-poll paths, validates the model against an allowlist, and forwards to `api.runflow.io` with your key injected. **Always add an `authenticate` hook in production.** The CSRF defaults block browser drive-by attacks but not direct server-to-server callers: anyone with your proxy URL can hit it from `curl`, a script, or another server and spend your Runflow budget against any allowed model. The hook is shown under [Auth, rate limit, telemetry hooks](#auth-rate-limit-telemetry-hooks) below. ### Next.js App Router ```ts // app/api/runflow/[...path]/route.ts import { runflowProxy } from "@runflow-io/proxy"; export const { GET, POST } = runflowProxy({ apiKey: process.env.RUNFLOW_API_KEY!, authenticate: async (req) => { const session = await getSession(req); if (!session) return null; // returns 401 return { userId: session.userId }; }, }); ``` ### Hono, Cloudflare Workers, SvelteKit, Bun, Deno ```ts import { runflowProxy } from "@runflow-io/proxy"; const handler = runflowProxy({ apiKey: process.env.RUNFLOW_API_KEY! }); // Hono app.all("/api/runflow/*", (c) => handler(c.req.raw)); // Cloudflare Workers export default { fetch: (req: Request) => handler(req) }; ``` ### Express, Fastify, classic Node `(req, res)` ```ts import { runflowProxyNode } from "@runflow-io/proxy/node"; app.use("/api/runflow", runflowProxyNode({ apiKey: process.env.RUNFLOW_API_KEY!, })); ``` ### Auth, rate limit, telemetry hooks The proxy ships safe defaults (model allowlist, 32 KB body cap, 30 s upstream timeout, masked upstream errors). Layer your own auth, rate limit, and observability with hooks: ```ts runflowProxy({ apiKey: process.env.RUNFLOW_API_KEY!, authenticate: async (req) => { const session = await getSession(req); if (!session) return null; // returns 401 return { userId: session.userId, context: { plan: session.plan } }; }, rateLimit: async ({ auth }) => { const hits = await redis.incr(`rf:${auth?.userId}`); if (hits > 100) return { status: 429, message: "Slow down", retryAfter: 60 }; }, allowedModels: (auth) => { const plan = (auth?.context as { plan?: string })?.plan; return plan === "pro" ? ["runflow/background-removal", "google/nano-banana-pro/edit"] : ["runflow/background-removal"]; }, onRun: async ({ runId, model, auth }) => { await db.runs.insert({ runId, model, userId: auth?.userId }); }, }); ``` ### Path contract The proxy accepts only these paths: | Method | Path | Purpose | |---|---|---| | POST | `/v1/models/{owner}/{slug...}/runs` | Dispatch a run. | | GET | `/v1/runs/{uuid}` | Poll a run. | | GET | `/v1/health` | Public health. | Everything else returns `403 Not allowed`. Run IDs are validated as UUIDv4-shape to block path traversal. ### CSRF defaults Non-GET requests are checked against the proxy's `allowedOrigins` policy. The default `"same-origin"` accepts only requests whose `Origin` host matches the request `Host` header. Pass an array of full origins (`["https://example.com"]`) to allow specific third-party callers. Two carveouts to know about: 1. **No `Origin` header = pass.** Server-to-server callers (curl, scripts, other backends) do not send `Origin`, and the proxy lets them through this gate by design. The `authenticate` hook is the only thing that can block them. 2. **`allowedOrigins: false` is dangerous.** If your `authenticate` hook reads cookies or session, disabling the origin check lets any third-party site trigger paid runs in a logged-in user's name. Prefer adding the third-party origin to the array. By default the proxy also requires `Content-Type: application/json` on non-GET requests so a malicious page cannot drain credentials via a `text/plain` CORS simple request. Set `requireJsonContentType: false` only if you proxy non-JSON workloads. ## Related Drop the Studio UI on your site. Bearer header, key rotation. Lifecycle and statuses. Status codes and the error envelope. --- # Pick a model ## What you'll do Match a task to the right Runflow model in under a minute. ## Decision tree | Task | Reach for | Why | |---|---|---| | Replace a product photo background | [`runflow/background-replace`](/models/runflow/background-replace) | Preserves the foreground subject, matches new lighting. | | Remove a background only | [`bria/background/remove`](/models/bria/background/remove) | Cheap and fast. | | Remove a person, sign, or logo | [`runflow/object-removal`](/models/runflow/object-removal) | Inpaint with no prompt needed. | | Change eye color | [`runflow/eye-color`](/models/runflow/eye-color) | Targeted edit, identity-preserving. | | Edit any image with a prompt | [`google/nano-banana-2/edit`](/models/google/nano-banana-2/edit) or [`black-forest-labs/flux-pro/kontext`](/models/black-forest-labs/flux-pro/kontext) | Best general-purpose edit. | | Generate an image from text | [`google/nano-banana-pro`](/models/google/nano-banana-pro) | High-quality general-purpose text-to-image. | | Generate a video from text | [`google/veo3.1`](/models/google/veo3.1) or [`bytedance/seedance/2.0/text-to-video`](/models/bytedance/seedance/2.0/text-to-video) | veo3.1 for cinematic, seedance for speed. | | Animate a still image | [`google/veo3.1/image-to-video`](/models/google/veo3.1/image-to-video) | Image-to-video conditioning. | | Add character to a video | [`heygen/v3/video-agent`](/models/heygen/v3/video-agent) | Avatar-driven. | | Generate speech | [`elevenlabs/tts/eleven-v3`](/models/elevenlabs/tts/eleven-v3) | High-fidelity TTS. | | Upscale a photo | [`topaz/upscale/image`](/models/topaz/upscale/image) | Up to 4x with detail recovery. | Browse the full catalog in [/models](/models). ## Three things to check before you pick 1. **Pricing unit.** `$/request` (flat) vs `$/megapixel` vs `$/second` (video). Map to your average asset size. 2. **Result delivery.** Runs are asynchronous by default. Use [callbacks](/concepts/callbacks) or polling unless the model page documents `sync_mode` and you intentionally want inline output. 3. **Inputs.** Most edit models accept either a public URL or an upload. Read the model page for required input keys. ## Try one Pick a model from the table, open its page, and copy the curl block. The samples come straight from the per-model `llms.txt` and most use the literal string `Bearer RUNFLOW_API_KEY` rather than an env-var. Substitute your actual token before running, or change the header to `Bearer $RUNFLOW_API_KEY` (curl) / `Bearer ${process.env.RUNFLOW_API_KEY}` (Node) / `f"Bearer {os.environ['RUNFLOW_API_KEY']}"` (Python). ## Related Full filterable catalog. 5-minute walkthrough. --- # Upload large files ## What you'll do Upload a video or large image directly to Runflow's storage and reference the resulting asset by ID in subsequent run requests. ## When to use this `POST /v1/asset-uploads` accepts `size_bytes` up to 50 MiB (52,428,800 bytes); larger uploads are rejected at create time. | Input size | Approach | |---|---| | < 1 MiB | Inline base64 or a public URL is fine. | | 1 MiB to 50 MiB | Inline if convenient, presigned upload if you control the server. | | > 50 MiB | Not supported by the asset-upload API. Host the file at a public URL and pass that URL on the run. | ## Prerequisites - A Runflow API key. - The file on disk or in memory. ## Steps Get a presigned URL. Tell Runflow the filename, MIME type, and exact byte size. ```bash curl -X POST https://api.runflow.io/v1/asset-uploads \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "filename": "perfume-bottle.webp", "mime_type": "image/webp", "size_bytes": 2400000 }' ``` Response (201 Created): ```json { "asset_id": "01J0...", "upload_url": "https://storage.runflow.io/..." } ``` Use the URL exactly as returned. No auth header, the presigned URL carries the credentials. ```bash curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: image/webp" \ --upload-file ./perfume-bottle.webp ``` Tell Runflow the upload is complete. The body is optional; pass `folder_id` to file the asset under a folder. ```bash curl -X POST https://api.runflow.io/v1/asset-uploads/{asset_upload_id}/confirmations \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` Response (201 Created) is the full Asset record. The fields you'll most often read: ```json { "id": "01J0...", "name": "perfume-bottle.webp", "mime_type": "image/webp", "size_bytes": 2400000, "url": "https://...", "thumbnail_url": "https://...", "asset_type": "image", "folder_id": null, "created_at": "2026-05-06T10:00:00Z" } ``` Reference it by `id` (or by `url` if the model accepts a public URL): ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "asset_id": "01J0...", "prompt": "white studio" }, "callback_url": "https://your-server.com/webhook" }' ``` ## Verify it worked ```bash curl https://api.runflow.io/v1/assets/{id} \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` A `200` with the Asset payload confirms it is usable. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | `403` on the PUT | Presigned URL expired | Re-create with `POST /v1/asset-uploads`. | | `422` on create | Size or MIME type rejected | `size_bytes` max is 50 MiB; check `mime_type` against the supported list at [`GET /v1/assets/types`](/api-reference/media/list-asset-types). | | Asset never confirmed | Confirmation step skipped | POST to the confirmations endpoint. | ## Related List, get, delete assets. Organize uploads. --- # Verify callback signatures ## What you'll do Reject spoofed POSTs to your webhook by verifying the `Runflow-Signature` HMAC header against a shared secret. ## Prerequisites - A Runflow API key. - A webhook handler (see [Handle async callbacks](/guides/handle-async-callbacks)). ## Steps ```bash curl -X POST https://api.runflow.io/v1/callback-secrets \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "production" }' ``` Response: ```json { "id": "01J0...", "label": "production", "plain_secret": "whsec_..." } ``` Save `plain_secret` to your secret manager. It is shown once. Runflow sends `Runflow-Signature: ` where the value is `HMAC-SHA256(secret, raw_body)`. The hex string is 64 chars (SHA-256 digest size). ```javascript Express import crypto from "crypto"; import express from "express"; const app = express(); // Use raw body, not parsed JSON, for HMAC. app.use(express.raw({ type: "application/json" })); const SIG_LEN = 64; // hex chars in HMAC-SHA256 app.post("/webhook/runflow", (req, res) => { const signature = req.header("Runflow-Signature") || ""; if (!/^[0-9a-f]{64}$/.test(signature)) { return res.sendStatus(401); } const expected = crypto .createHmac("sha256", process.env.RUNFLOW_WEBHOOK_SECRET) .update(req.body) .digest("hex"); const a = Buffer.from(signature, "hex"); const b = Buffer.from(expected, "hex"); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) { return res.sendStatus(401); } const payload = JSON.parse(req.body.toString("utf8")); // ...persist payload, return 200... res.sendStatus(200); }); ``` ```python FastAPI import hmac, hashlib, os, re from fastapi import FastAPI, Request, HTTPException app = FastAPI() SECRET = os.environ["RUNFLOW_WEBHOOK_SECRET"].encode() HEX_64 = re.compile(r"^[0-9a-f]{64}$") @app.post("/webhook/runflow") async def runflow_webhook(req: Request): body = await req.body() signature = req.headers.get("Runflow-Signature", "") if not HEX_64.match(signature): raise HTTPException(401, "bad signature") expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest() if not hmac.compare_digest(signature, expected): raise HTTPException(401, "bad signature") payload = await req.json() # ...persist, return 200... return {"ok": True} ``` Always compare with `crypto.timingSafeEqual` (Node) or `hmac.compare_digest` (Python). Plain `==` leaks timing info. Both inputs must be the same length, hence the regex pre-check above. ## Verify it worked Send a test callback (re-deliver one): ```bash curl -X POST https://api.runflow.io/v1/runs/{run_id}/callback-redeliveries \ -H "Authorization: Bearer $RUNFLOW_API_KEY" ``` Your handler logs should show `200`. Now send a forged POST without the header. Your handler should return `401`. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Every callback fails verification | Hashing parsed JSON instead of raw body | Use the raw bytes Runflow sent. | | Signatures match in dev, fail in prod | Different secret per env | Rotate, redeploy. | | `Runflow-Signature` header missing | No callback secret registered for this endpoint | POST to `/v1/callback-secrets` first. | ## Rotation Create a second secret. Both verify in parallel during cutover. Delete the old secret once traffic confirms the new one works. ## Related Handler structure. Pattern, retries. --- # Runflow Start AI image and video runs with one HTTP call, then receive results by callback or polling. The Runflow API exposes a curated catalog of [45+ models](/models) from Runflow, Google, Alibaba, OpenAI, and others. Auth is HTTP Bearer. Live count: [`GET /v1/public/models`](https://api.runflow.io/v1/public/models). ## Five-minute quickstart ```bash export RUNFLOW_API_KEY="your_key" curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "white studio", "image_url": "https://..." } }' ``` Get a key at [app.runflow.io](https://app.runflow.io/settings/api-keys). Full walkthrough at [Quickstart](/quickstart). ## Where to go next 45+ models, 6 categories, prices and endpoints. First call in under 5 minutes. Generated from the public OpenAPI spec. Skill, llms.txt, OpenAPI catalog. ## Concepts Bearer header. Callbacks, polling, statuses. Async result delivery. Status codes, error envelope. Per-request, per-second, per-megapixel. Headers and backoff. ## Guides Decision tree from task to model. Many inputs, one request. Webhook handler patterns. HMAC verification. --- # Happy Horse Image-to-Video Pricing: **$0.14/second**. Endpoint: `POST /v1/models/alibaba/happy-horse/image-to-video/runs`. Alibaba's #1-ranked Happy Horse 1.0 - generate 1080p video with synchronized native audio and multilingual lip-sync from text prompts or images. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/happy-horse/image-to-video/runs - **Model ID**: alibaba/happy-horse/image-to-video - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.14/second - **Note**: Per second of generated video (720p baseline) # Happy Horse Image-to-Video API **Endpoint:** `POST /v1/models/alibaba/happy-horse/image-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/happy-horse/image-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "She smiles warmly and looks toward the camera, gentle breeze in her hair", "duration": 5, "image_url": "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=1024&q=80", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/happy-horse/image-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "She smiles warmly and looks toward the camera, gentle breeze in her hair", "duration": 5, "image_url": "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=1024&q=80", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/happy-horse/image-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "She smiles warmly and looks toward the camera, gentle breeze in her hair", "duration": 5, "image_url": "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=1024&q=80", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the first frame image. Formats: JPEG, JPG, PNG, BMP, WEBP. Dimensions must be at least 300px. Aspect ratio must be between 1:2.5 and 2.5:1. Max 10 MB. | | `duration` | integer | optional | Any | Output video duration in seconds (3-15). | | `prompt` | string | optional | Any | Optional text prompt guiding the animation. Max 2500 characters. | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | | `resolution` | string | optional | `720p`, `1080p` | Output video resolution tier. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/happy-horse/image-to-video) - [API Reference](https://app.runflow.io/models/alibaba/happy-horse/image-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Happy Horse Reference-to-Video Pricing: **$0.14/second**. Endpoint: `POST /v1/models/alibaba/happy-horse/reference-to-video/runs`. Generate 1080p video with synchronized native audio from a text prompt and references. Aspect ratios: 16:9, 9:16, 1:1, 4:3, 3:4. Duration: 3–15s. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/happy-horse/reference-to-video/runs - **Model ID**: alibaba/happy-horse/reference-to-video - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.14/second - **Note**: Per second of generated video (720p baseline) # Happy Horse Reference-to-Video API **Endpoint:** `POST /v1/models/alibaba/happy-horse/reference-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/happy-horse/reference-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "character1 walks confidently down a neon-lit Tokyo street at night, cinematic lighting, smooth tracking shot", "duration": 5, "image_urls": [ "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=1024&q=80" ], "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/happy-horse/reference-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "character1 walks confidently down a neon-lit Tokyo street at night, cinematic lighting, smooth tracking shot", "duration": 5, "image_urls": [ "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=1024&q=80" ], "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/happy-horse/reference-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "character1 walks confidently down a neon-lit Tokyo street at night, cinematic lighting, smooth tracking shot", "duration": 5, "image_urls": [ "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=1024&q=80" ], "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the desired video. Reference subjects from your images using ``character1``, ``character2``, ... up to ``character9`` (the order matches the order of ``image_urls``). Max 2500 characters. | | `image_urls` | image_list | required | Any | Reference images for subject consistency (1-9 images). Formats: JPEG, JPG, PNG, WEBP. Shortest side must be at least 400 px (720P or higher recommended). Max 10 MB each. | | `aspect_ratio` | string | optional | `16:9`, `9:16`, `1:1`, `4:3`, `3:4` | Aspect ratio of the generated video. | | `duration` | integer | optional | Any | Output video duration in seconds (3-15). | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | | `resolution` | string | optional | `720p`, `1080p` | Output video resolution tier. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/happy-horse/reference-to-video) - [API Reference](https://app.runflow.io/models/alibaba/happy-horse/reference-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Happy Horse Text-to-Video Pricing: **$0.14/second**. Endpoint: `POST /v1/models/alibaba/happy-horse/text-to-video/runs`. Generate 1080p video with synchronized native audio from a text prompt. Aspect ratios: 16:9, 9:16, 1:1, 4:3, 3:4. Duration: 3–15s. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/happy-horse/text-to-video/runs - **Model ID**: alibaba/happy-horse/text-to-video - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.14/second - **Note**: Per second of generated video (720p baseline) # Happy Horse Text-to-Video API **Endpoint:** `POST /v1/models/alibaba/happy-horse/text-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/happy-horse/text-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A cinematic shot of a hummingbird hovering near a vibrant red flower in slow motion, golden hour lighting, shallow depth of field", "duration": 5, "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/happy-horse/text-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A cinematic shot of a hummingbird hovering near a vibrant red flower in slow motion, golden hour lighting, shallow depth of field", "duration": 5, "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/happy-horse/text-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A cinematic shot of a hummingbird hovering near a vibrant red flower in slow motion, golden hour lighting, shallow depth of field", "duration": 5, "resolution": "720p", "aspect_ratio": "16:9" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the desired video. Max 2500 characters. | | `aspect_ratio` | string | optional | `16:9`, `9:16`, `1:1`, `4:3`, `3:4` | Aspect ratio of the generated video. | | `duration` | integer | optional | Any | Output video duration in seconds (3-15). | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | | `resolution` | string | optional | `720p`, `1080p` | Output video resolution tier. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/happy-horse/text-to-video) - [API Reference](https://app.runflow.io/models/alibaba/happy-horse/text-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Happy Horse Video Edit Pricing: **$0.28/second**. Endpoint: `POST /v1/models/alibaba/happy-horse/video-edit/runs`. HappyHorse video editing supports advanced video editing through natural language instructions. It allows for local or global editing of video elements using up to 5 reference images. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/happy-horse/video-edit/runs - **Model ID**: alibaba/happy-horse/video-edit - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.28/second - **Note**: Per second of edited video (720p baseline) # Happy Horse Video Edit API **Endpoint:** `POST /v1/models/alibaba/happy-horse/video-edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/happy-horse/video-edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Replace the underwater background with deep cosmic space, the jellyfish becomes a glowing nebula creature among shimmering stars", "video_url": "https://public.runflow.io/images/models/_shared/source-videos/jellyfish-1080.mp4", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/happy-horse/video-edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Replace the underwater background with deep cosmic space, the jellyfish becomes a glowing nebula creature among shimmering stars", "video_url": "https://public.runflow.io/images/models/_shared/source-videos/jellyfish-1080.mp4", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/happy-horse/video-edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Replace the underwater background with deep cosmic space, the jellyfish becomes a glowing nebula creature among shimmering stars", "video_url": "https://public.runflow.io/images/models/_shared/source-videos/jellyfish-1080.mp4", "resolution": "720p" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the desired edit. Reference any supplied reference images using @Image1, @Image2, ... up to @Image5. Max 2500 characters. | | `video_url` | video | required | Any | URL of the source video to edit. Formats: MP4, MOV (H.264 recommended). Duration: 3-60 s. Longer side ≤ 2160 px, shorter side ≥ 320 px. Aspect ratio between 1:2.5 and 2.5:1. Frame rate > 8 fps. Max 100 MB. The output video preserves the source aspect ratio. Output duration matches the input video, capped at 15 s (longer inputs are truncated to the first 15 s). | | `audio_setting` | string | optional | `auto`, `origin` | Audio handling. 'auto': model decides whether to regenerate audio. 'origin': preserve the original audio from the input video. | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | | `reference_image_urls` | image_list | optional | Any | Optional reference images used to guide the edit (up to 5). Formats: JPEG, JPG, PNG, WEBP. Dimensions must be at least 300px. Aspect ratio between 1:2.5 and 2.5:1. Max 10 MB each. | | `resolution` | string | optional | `720p`, `1080p` | Output video resolution tier. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/happy-horse/video-edit) - [API Reference](https://app.runflow.io/models/alibaba/happy-horse/video-edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Qwen Image Edit 2511 Pricing: **$0.03/megapixel**. Endpoint: `POST /v1/models/alibaba/qwen-image-edit-2511/runs`. Advanced image editing model from Alibaba. Qwen Image Edit 2511 delivers superior text editing capabilities, multi-image support, and precise visual modifications through natural language instructions. Supports style transfer, object editing, and creative compositions. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/qwen-image-edit-2511/runs - **Model ID**: alibaba/qwen-image-edit-2511 - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.03/megapixel - **Note**: Default price # Qwen Image Edit 2511 API **Endpoint:** `POST /v1/models/alibaba/qwen-image-edit-2511/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/qwen-image-edit-2511/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Move this person to a Mars colony habitat...", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/qwen-image-edit-2511/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Move this person to a Mars colony habitat...", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/qwen-image-edit-2511/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Move this person to a Mars colony habitat...", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The prompt to edit the image with. | | `num_images` | integer | optional | Any | The number of images to generate. | | `acceleration` | string | optional | `none`, `regular`, `high` | The acceleration level to use. | | `image_size` | string | optional | Any | The size of the generated image. If None, uses the input image dimensions. | | `enable_safety_checker` | boolean | optional | Any | If set to true, the safety checker will be enabled. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | The format of the generated image. | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI. | | `guidance_scale` | float | optional | Any | The guidance scale to use for the image generation. | | `seed` | integer | optional | Any | The same seed and the same prompt given to the same version of the model will output the same image every time. | | `image_urls` | image_list | required | Any | The URLs of the images to edit. | | `negative_prompt` | string | optional | Any | The negative prompt to generate an image from. | | `num_inference_steps` | integer | optional | Any | The number of inference steps to perform. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Seed of the generated Image. It will be the same value of the one passed in the input or the randomly generated that was used in case none was passed. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/qwen-image-edit-2511) - [API Reference](https://app.runflow.io/models/alibaba/qwen-image-edit-2511?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Wan 2.7 - Image to Video Pricing: **$0.1/second**. Endpoint: `POST /v1/models/alibaba/wan/v2.7/image-to-video/runs`. Wan 2.7 delivers enhanced motion smoothness, superior scene fidelity, and greater visual coherence. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/wan/v2.7/image-to-video/runs - **Model ID**: alibaba/wan/v2.7/image-to-video - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.1/second - **Note**: Per second of video # Wan 2.7 - Image to Video API **Endpoint:** `POST /v1/models/alibaba/wan/v2.7/image-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/image-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Product rotating slowly on a display pedestal, soft studio lighting, professional product photography", "duration": 5, "image_url": "https://public.runflow.io/images/models/_base/product-sneaker.png", "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/image-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Product rotating slowly on a display pedestal, soft studio lighting, professional product photography", "duration": 5, "image_url": "https://public.runflow.io/images/models/_base/product-sneaker.png", "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/wan/v2.7/image-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Product rotating slowly on a display pedestal, soft studio lighting, professional product photography", "duration": 5, "image_url": "https://public.runflow.io/images/models/_base/product-sneaker.png", "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | optional | Any | | | `image_url` | image | optional | Any | | | `end_image_url` | image | optional | Any | | | `video_url` | video | optional | Any | | | `audio_url` | file | optional | Any | | | `resolution` | string | optional | `720p`, `1080p` | | | `duration` | integer | optional | Any | | | `negative_prompt` | string | optional | Any | | | `enable_prompt_expansion` | boolean | optional | Any | | | `seed` | integer | optional | Any | | | `enable_safety_checker` | boolean | optional | Any | | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/wan/v2.7/image-to-video) - [API Reference](https://app.runflow.io/models/alibaba/wan/v2.7/image-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Wan 2.7 Pro Edit Pricing: **$0.075/image**. Endpoint: `POST /v1/models/alibaba/wan/v2.7/pro/edit/runs`. Edit and transform images using text instructions with the WAN 2.7 Pro model for precise, professional-grade image modifications. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/wan/v2.7/pro/edit/runs - **Model ID**: alibaba/wan/v2.7/pro/edit - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.075/image - **Note**: Per image # Wan 2.7 Pro Edit API **Endpoint:** `POST /v1/models/alibaba/wan/v2.7/pro/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/pro/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Turn the office into a cozy mountain cabin: snow falling outside the windows, exposed wooden beams on ceiling, stone fireplace crackling, warm amber light", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/pro/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Turn the office into a cozy mountain cabin: snow falling outside the windows, exposed wooden beams on ceiling, stone fireplace crackling, warm amber light", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/wan/v2.7/pro/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Turn the office into a cozy mountain cabin: snow falling outside the windows, exposed wooden beams on ceiling, stone fireplace crackling, warm amber light", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the desired image edit. Supports Chinese and English. | | `image_urls` | image_list | required | Any | Reference images for editing (1-4 images required). Order matters: reference them as image 1, image 2, image 3, image 4 in the prompt. | | `negative_prompt` | string | optional | Any | Content to avoid in the generated image. Max 500 characters. | | `image_size` | string | optional | Any | Output image size. Uses fal image size presets or explicit dimensions and is converted to DashScope size format. | | `num_images` | integer | optional | Any | Number of images to generate (1-4). | | `enable_prompt_expansion` | boolean | optional | Any | Enable DashScope prompt expansion. Supported only for image edit mode. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | The seed used for generation. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/wan/v2.7/pro/edit) - [API Reference](https://app.runflow.io/models/alibaba/wan/v2.7/pro/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Wan 2.7 Text to Video Pricing: **$0.1/second**. Endpoint: `POST /v1/models/alibaba/wan/v2.7/text-to-video/runs`. Wan 2.7 is the latest generation AI video model, delivering enhanced motion smoothness, superior scene fidelity, and greater visual coherence. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/alibaba/wan/v2.7/text-to-video/runs - **Model ID**: alibaba/wan/v2.7/text-to-video - **Provider**: Alibaba - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.1/second - **Note**: Per second of video # Wan 2.7 Text to Video API **Endpoint:** `POST /v1/models/alibaba/wan/v2.7/text-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/text-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Aerial drone shot pulling back slowly to reveal a vibrant coral reef teeming with tropical fish, crystal clear turquoise water, sunlight rays penetrating the surface", "duration": 5, "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/alibaba/wan/v2.7/text-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Aerial drone shot pulling back slowly to reveal a vibrant coral reef teeming with tropical fish, crystal clear turquoise water, sunlight rays penetrating the surface", "duration": 5, "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/alibaba/wan/v2.7/text-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Aerial drone shot pulling back slowly to reveal a vibrant coral reef teeming with tropical fish, crystal clear turquoise water, sunlight rays penetrating the surface", "duration": 5, "resolution": "1080p" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the desired video. Max 5000 characters. | | `audio_url` | file | optional | Any | URL of driving audio. Supports WAV and MP3. Duration: 3-30s. Max 15 MB. If not provided, the model auto-generates matching background music. | | `aspect_ratio` | string | optional | `16:9`, `9:16`, `1:1`, `4:3`, `3:4` | Aspect ratio of the generated video. | | `resolution` | string | optional | `720p`, `1080p` | Output video resolution tier. | | `duration` | integer | optional | `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15` | Output video duration in seconds (2-15). | | `negative_prompt` | string | optional | Any | Content to avoid in the video. Max 500 characters. | | `enable_prompt_expansion` | boolean | optional | Any | Enable intelligent prompt rewriting. | | `seed` | integer | optional | Any | Random seed for reproducibility (0-2147483647). | | `enable_safety_checker` | boolean | optional | Any | Enable content moderation for input and output. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | The seed used for generation. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/alibaba/wan/v2.7/text-to-video) - [API Reference](https://app.runflow.io/models/alibaba/wan/v2.7/text-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX 1.1 [pro] Ultra Pricing: **$0.06/image**. Endpoint: `POST /v1/models/black-forest-labs/flux-1-1-pro-ultra/runs`. Ultra-resolution variant of FLUX 1.1 [pro] with 4MP+ outputs and richer detail. Drop-in upgrade when the standard FLUX 1.1 [pro] resolution is the bottleneck. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro-ultra/runs - **Model ID**: black-forest-labs/flux-1-1-pro-ultra - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.06/image - **Note**: per image, ultra resolution # FLUX 1.1 [pro] Ultra API **Endpoint:** `POST /v1/models/black-forest-labs/flux-1-1-pro-ultra/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro-ultra/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro-ultra/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro-ultra/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text description of the image to generate. | | `seed` | integer | optional | Any | Random positive integer for reproducible results. Leave blank to randomise. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. One entry per generated artifact with url/type/width/height/content_type. | | `seed` | json | Deterministic seed used for generation, or null if not returned. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if flagged as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-1-1-pro-ultra) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-1-1-pro-ultra?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX 1.1 [pro] Pricing: **$0.04/image**. Endpoint: `POST /v1/models/black-forest-labs/flux-1-1-pro/runs`. Black Forest Labs' production-grade FLUX 1.1. State-of-the-art prompt adherence and photoreal quality at competitive per-image pricing. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro/runs - **Model ID**: black-forest-labs/flux-1-1-pro - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.04/image - **Note**: per image # FLUX 1.1 [pro] API **Endpoint:** `POST /v1/models/black-forest-labs/flux-1-1-pro/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-1-1-pro/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "a serene japanese garden with cherry blossoms at dawn" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text description of the image to generate. | | `seed` | integer | optional | Any | Random positive integer for reproducible results. Leave blank to randomise. | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9` | Output aspect ratio preset. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. One entry per generated artifact with url/type/width/height/content_type. | | `seed` | json | Deterministic seed used for generation, or null if not returned. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if flagged as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-1-1-pro) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-1-1-pro?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX.1 [dev] Edit Pricing: **$0.025/image**. Endpoint: `POST /v1/models/black-forest-labs/flux-1-dev/edit/runs`. Image-to-image edits with FLUX.1 [dev]. Pass a source image plus a prompt and a transformation strength to redirect the composition while preserving subject identity. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-1-dev/edit/runs - **Model ID**: black-forest-labs/flux-1-dev/edit - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.025/image - **Note**: per image at 1024x1024 # FLUX.1 [dev] Edit API **Endpoint:** `POST /v1/models/black-forest-labs/flux-1-dev/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-dev/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "steps": 28, "prompt": "Transform the engineer into a samurai warrior with traditional kabuto armor and a katana, dramatic lighting, photoreal cinematic style", "strength": 0.85, "image_b64": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-1-dev/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "steps": 28, "prompt": "Transform the engineer into a samurai warrior with traditional kabuto armor and a katana, dramatic lighting, photoreal cinematic style", "strength": 0.85, "image_b64": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-1-dev/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "steps": 28, "prompt": "Transform the engineer into a samurai warrior with traditional kabuto armor and a katana, dramatic lighting, photoreal cinematic style", "strength": 0.85, "image_b64": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text description of the image to generate. | | `strength` | float | optional | Any | How much to transform the source image. 0 keeps it intact; 1 ignores it. 0.5-0.8 typical. | | `steps` | integer | optional | Any | Number of diffusion steps. Higher = slightly more quality, slower generation. | | `guidance` | float | optional | Any | Higher values make the model follow the prompt more strictly. | | `seed` | integer | optional | Any | Random positive integer for reproducible results. Leave blank to randomise. | | `image_b64` | image | required | Any | Base64-encoded image or image URL. Used as the reference for image-to-image edits. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. One entry per generated artifact with url/type/width/height/content_type. | | `seed` | json | Deterministic seed used for generation, or null if not returned. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if flagged as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-1-dev/edit) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-1-dev/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX.2 [klein] 4B Pricing: **$0.005/megapixel**. Endpoint: `POST /v1/models/black-forest-labs/flux-2-klein-4b/runs`. Compact 4-billion parameter text-to-image model from Black Forest Labs. Step-distilled to 4 inference steps for sub-second generation. Unifies text-to-image and image editing in a single architecture with Apache 2.0 licensing. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/runs - **Model ID**: black-forest-labs/flux-2-klein-4b - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.005/megapixel - **Note**: Per megapixel # FLUX.2 [klein] 4B API **Endpoint:** `POST /v1/models/black-forest-labs/flux-2-klein-4b/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A vintage travel poster for 'RUNFLOW AIRWAYS' featuring a retro DC-3 airplane flying over snow-capped mountains at sunset, bold art deco typography, warm orange and teal color palette, textured paper grain, 1940s airline advertising style" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A vintage travel poster for 'RUNFLOW AIRWAYS' featuring a retro DC-3 airplane flying over snow-capped mountains at sunset, bold art deco typography, warm orange and teal color palette, textured paper grain, 1940s airline advertising style" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A vintage travel poster for 'RUNFLOW AIRWAYS' featuring a retro DC-3 airplane flying over snow-capped mountains at sunset, bold art deco typography, warm orange and teal color palette, textured paper grain, 1940s airline advertising style" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text description of the image to generate. | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9` | Size preset for the generated image. Alternatively use width/height for custom dimensions. | | `num_images` | integer | optional | Any | Number of images to generate (1-4). | | `num_inference_steps` | integer | optional | Any | Number of denoising steps. Default 4 for distilled variant, use higher for base variant. | | `seed` | integer | optional | Any | Random seed for reproducible generation. Leave empty for random. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | Output image format. | | `enable_safety_checker` | boolean | optional | Any | Enable NSFW content filtering. | | `sync_mode` | boolean | optional | Any | Wait for the result synchronously instead of returning a request ID. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | The seed used for generation. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-2-klein-4b) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-2-klein-4b?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX.2 [klein] 4B Edit Pricing: **$0.005/image**. Endpoint: `POST /v1/models/black-forest-labs/flux-2-klein-4b/edit/runs`. Fast image-edit endpoint backed by FLUX.2 [klein] 4B. Pass a base64 source image with the prompt for low-latency edits. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/edit/runs - **Model ID**: black-forest-labs/flux-2-klein-4b/edit - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.005/image - **Note**: per image, 1024x1024, up to 25 steps # FLUX.2 [klein] 4B Edit API **Endpoint:** `POST /v1/models/black-forest-labs/flux-2-klein-4b/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "steps": 4, "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "steps": 4, "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-4b/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "steps": 4, "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | A text description of the image you want to generate. | | `steps` | integer | optional | Any | Number of diffusion steps. More steps = slightly higher quality, longer generation. | | `seed` | integer | optional | Any | Random positive integer for reproducible results. Leave blank to randomise. | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9` | Output aspect ratio preset. | | `reference_image_1` | image | optional | Any | Reference image 1 for FLUX.2 multi-reference edit (1-4 images). | | `reference_image_2` | image | optional | Any | Reference image 2 for FLUX.2 multi-reference edit (1-4 images). | | `reference_image_3` | image | optional | Any | Reference image 3 for FLUX.2 multi-reference edit (1-4 images). | | `reference_image_4` | image | optional | Any | Reference image 4 for FLUX.2 multi-reference edit (1-4 images). | | `image_urls` | image_list | optional | Any | Source image URLs for editing (1-4). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/content_type. | | `seed` | json | Deterministic seed used for generation, or null if not returned. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if flagged as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-2-klein-4b/edit) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-2-klein-4b/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX.2 [klein] 9B Pricing: **$0.006/megapixel**. Endpoint: `POST /v1/models/black-forest-labs/flux-2-klein-9b/runs`. Text-to-image generation with FLUX.2 [klein] 9B from Black Forest Labs. Enhanced realism, crisper text generation, and native editing capabilities. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/runs - **Model ID**: black-forest-labs/flux-2-klein-9b - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.006/megapixel - **Note**: Per megapixel # FLUX.2 [klein] 9B API **Endpoint:** `POST /v1/models/black-forest-labs/flux-2-klein-9b/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A neon-lit Tokyo alleyway at night with a vending machine glowing amber, steam rising from grates, wet reflections on asphalt, cinematic", "image_size": "landscape_4_3", "output_format": "png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A neon-lit Tokyo alleyway at night with a vending machine glowing amber, steam rising from grates, wet reflections on asphalt, cinematic", "image_size": "landscape_4_3", "output_format": "png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A neon-lit Tokyo alleyway at night with a vending machine glowing amber, steam rising from grates, wet reflections on asphalt, cinematic", "image_size": "landscape_4_3", "output_format": "png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | | | `seed` | integer | optional | Any | | | `num_inference_steps` | integer | optional | Any | | | `image_size` | string | optional | Any | | | `num_images` | integer | optional | Any | | | `sync_mode` | boolean | optional | Any | | | `enable_safety_checker` | boolean | optional | Any | | | `output_format` | string | optional | `jpeg`, `png`, `webp` | | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-2-klein-9b) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-2-klein-9b?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX.2 [klein] 9B - Edit Pricing: **$0.011/megapixel**. Endpoint: `POST /v1/models/black-forest-labs/flux-2-klein-9b/edit/runs`. Image-to-image editing with FLUX.2 [klein] 9B from Black Forest Labs. Precise modifications using natural language descriptions and hex color control. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/edit/runs - **Model ID**: black-forest-labs/flux-2-klein-9b/edit - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.011/megapixel - **Note**: Per megapixel (input + output) # FLUX.2 [klein] 9B - Edit API **Endpoint:** `POST /v1/models/black-forest-labs/flux-2-klein-9b/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad, everything else stays the same", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad, everything else stays the same", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-2-klein-9b/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Replace the computer monitors with a large glass aquarium full of colorful tropical fish and coral, the keyboard sits on a bed of white sand, small hermit crabs crawl on the mousepad, everything else stays the same", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | | | `image_urls` | image_list | required | Any | | | `seed` | integer | optional | Any | | | `num_inference_steps` | integer | optional | Any | | | `image_size` | string | optional | Any | | | `num_images` | integer | optional | Any | | | `sync_mode` | boolean | optional | Any | | | `enable_safety_checker` | boolean | optional | Any | | | `output_format` | string | optional | `jpeg`, `png`, `webp` | | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-2-klein-9b/edit) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-2-klein-9b/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # FLUX Kontext Pricing: **$0.04/image**. Endpoint: `POST /v1/models/black-forest-labs/flux-pro/kontext/runs`. FLUX.1 Kontext [pro] handles both text and reference images as inputs, seamlessly enabling targeted, local edits and complex transformations of entire scenes. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/black-forest-labs/flux-pro/kontext/runs - **Model ID**: black-forest-labs/flux-pro/kontext - **Provider**: Black Forest Labs - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.04/image - **Note**: ~25 runs per $1 # FLUX Kontext API **Endpoint:** `POST /v1/models/black-forest-labs/flux-pro/kontext/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/black-forest-labs/flux-pro/kontext/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Add a fluffy orange tabby cat sleeping on the desk between the two monitors, curled up on a small cushion, one paw draped over the keyboard. The cat is illuminated by the monitor glow. Everything else stays exactly the same.", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/black-forest-labs/flux-pro/kontext/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Add a fluffy orange tabby cat sleeping on the desk between the two monitors, curled up on a small cushion, one paw draped over the keyboard. The cat is illuminated by the monitor glow. Everything else stays exactly the same.", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/black-forest-labs/flux-pro/kontext/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Add a fluffy orange tabby cat sleeping on the desk between the two monitors, curled up on a small cushion, one paw draped over the keyboard. The cat is illuminated by the monitor glow. Everything else stays exactly the same.", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Editing instruction describing the desired transformation. | | `image_url` | image | required | Any | URL of the source image to edit. | | `aspect_ratio` | string | optional | `21:9`, `16:9`, `4:3`, `3:2`, `1:1`, `2:3`, `3:4`, `9:16`, `9:21` | Output aspect ratio. Options: square_hd, square, portrait_4_3, portrait_16_9, landscape_4_3, landscape_16_9. | | `num_images` | integer | optional | Any | Number of images to generate per request. Range: 1–4. Default: 1. | | `guidance_scale` | float | optional | Any | How closely the model follows the prompt. Range: 1–20. Default: 3.5. | | `output_format` | string | optional | `jpeg`, `png` | Output format. Options: "jpeg", "png". Default: "jpeg". | | `safety_tolerance` | string | optional | Any | Content moderation strictness. 1 = most strict, 6 = most permissive. Default: 2. | | `enhance_prompt` | boolean | optional | Any | Automatically enhance the prompt for better results. Default: false. | | `seed` | integer | optional | Any | Random seed for reproducible outputs. | | `sync_mode` | boolean | optional | Any | Return media as data URI without storing in request history. Default: false. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Seed of the generated Image. It will be the same value of the one passed in the input or the randomly generated that was used in case none was passed. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/black-forest-labs/flux-pro/kontext) - [API Reference](https://app.runflow.io/models/black-forest-labs/flux-pro/kontext?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Bria Background Remove Pricing: **$0.018/request**. Endpoint: `POST /v1/models/bria/background/remove/runs`. Bria RMBG 2.0 enables seamless removal of backgrounds from images, ideal for professional editing tasks. Trained exclusively on licensed data for safe and risk-free commercial use. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/bria/background/remove/runs - **Model ID**: bria/background/remove - **Provider**: Bria AI - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.018/request - **Note**: Usage-based, per generation # Bria Background Remove API **Endpoint:** `POST /v1/models/bria/background/remove/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/bria/background/remove/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/alibaba/qwen-image-2/pro/text-to-image/example-output-0.webp", "sync_mode": True }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/bria/background/remove/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "image_url": "https://public.runflow.io/images/models/alibaba/qwen-image-2/pro/text-to-image/example-output-0.webp", "sync_mode": true }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/bria/background/remove/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "image_url": "https://public.runflow.io/images/models/alibaba/qwen-image-2/pro/text-to-image/example-output-0.webp", "sync_mode": true }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | | `image_url` | image | required | Any | Input Image to erase from | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/bria/background/remove) - [API Reference](https://app.runflow.io/models/bria/background/remove?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # SDXL-Lightning Pricing: **$0.003/image**. Endpoint: `POST /v1/models/bytedance/sd-xl-lightning/runs`. ByteDance lightning-fast SDXL distillation. 4-step text-to-image at near-real-time latency. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/bytedance/sd-xl-lightning/runs - **Model ID**: bytedance/sd-xl-lightning - **Provider**: ByteDance - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.003/image - **Note**: per image, 1024x1024 # SDXL-Lightning API **Endpoint:** `POST /v1/models/bytedance/sd-xl-lightning/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/bytedance/sd-xl-lightning/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "a cyberpunk city street at night, neon signs glowing" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/bytedance/sd-xl-lightning/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "a cyberpunk city street at night, neon signs glowing" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/bytedance/sd-xl-lightning/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "a cyberpunk city street at night, neon signs glowing" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | A text description of the image you want to generate. | | `num_steps` | string | optional | `1`, `2`, `4`, `8` | Diffusion iterations. SDXL-Lightning is tuned for 4 steps. | | `seed` | integer | optional | Any | Random positive integer for reproducible results. Leave blank to randomise. | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9` | Output aspect ratio preset. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/content_type. | | `seed` | json | Deterministic seed used for generation, or null if not returned. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | | `nsfw_detected` | json | true if flagged as NSFW, false if cleared, null if not checked. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/bytedance/sd-xl-lightning) - [API Reference](https://app.runflow.io/models/bytedance/sd-xl-lightning?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # ElevenLabs TTS v3 Pricing: **$100/1m-tokens**. Endpoint: `POST /v1/models/elevenlabs/tts/eleven-v3/runs`. Generate text-to-speech audio using Eleven-v3 from ElevenLabs. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/elevenlabs/tts/eleven-v3/runs - **Model ID**: elevenlabs/tts/eleven-v3 - **Provider**: ElevenLabs - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $100/1m-tokens - **Note**: Usage-based, per 1000 characters # ElevenLabs TTS v3 API **Endpoint:** `POST /v1/models/elevenlabs/tts/eleven-v3/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/elevenlabs/tts/eleven-v3/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "text": "Welcome to the Runflow Lab! This week we shipped five new models and honestly, the hardest part was naming them. Turns out, all the good AI names are taken." }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/elevenlabs/tts/eleven-v3/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "text": "Welcome to the Runflow Lab! This week we shipped five new models and honestly, the hardest part was naming them. Turns out, all the good AI names are taken." }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/elevenlabs/tts/eleven-v3/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "text": "Welcome to the Runflow Lab! This week we shipped five new models and honestly, the hardest part was naming them. Turns out, all the good AI names are taken." }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `text` | string | required | Any | The text to convert to speech | | `voice` | string | optional | Any | The voice to use for speech generation | | `language_code` | string | optional | Any | Language code (ISO 639-1) used to enforce a language for the model. | | `stability` | float | optional | Any | Voice stability (0-1) | | `apply_text_normalization` | string | optional | `auto`, `on`, `off` | This parameter controls text normalization with three modes: 'auto', 'on', and 'off'. When set to 'auto', the system will automatically decide whether to apply text normalization (e.g., spelling out numbers). With 'on', text normalization will always be applied, while with 'off', it will be skipped. | | `timestamps` | boolean | optional | Any | Whether to return timestamps for each word in the generated speech | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/elevenlabs/tts/eleven-v3) - [API Reference](https://app.runflow.io/models/elevenlabs/tts/eleven-v3?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Gemini TTS Pricing: **$10/1m-tokens**. Endpoint: `POST /v1/models/google/gemini-tts/runs`. Google's Gemini TTS converts text to realistic audio. 30 voice presets, multi-speaker synthesis (up to 10 speakers), 24+ languages, and inline style markers for expressive control. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/google/gemini-tts/runs - **Model ID**: google/gemini-tts - **Provider**: Google - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $10/1m-tokens - **Note**: Per 1M tokens (flash, output-dominated) # Gemini TTS API **Endpoint:** `POST /v1/models/google/gemini-tts/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/google/gemini-tts/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": {}, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/google/gemini-tts/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": {}, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/google/gemini-tts/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": {}, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The text to convert to speech. Gemini TTS supports natural-language prompting for style, pace, accent, and emotional expression - include delivery instructions inline with the text (e.g. 'Say cheerfully: Have a wonderful day!'). For multi-speaker synthesis, prefix lines with speaker aliases defined in the speakers field (e.g. 'Alice: Hello!\nBob: Hi!'). Supports inline pace/style markers like [slowly], [whispering], [excited], [extremely fast]. | | `speakers` | json | optional | Any | Multi-speaker voice configuration. When set, enables multi-speaker synthesis where different parts of the text are spoken by different voices. Each speaker needs a voice and a speaker_id (alias) that matches prefixes in the prompt. Requires gemini-2.5-pro-tts or gemini-2.5-flash-tts model. Not supported with gemini-2.5-flash-lite-preview-tts. | | `output_format` | string | optional | `wav`, `mp3`, `ogg_opus` | Audio output format. mp3: compressed, small file size (recommended). wav: uncompressed PCM wrapped in WAV (24 kHz, 16-bit mono). ogg_opus: Ogg container with Opus codec, good quality-to-size ratio. | | `model` | string | optional | `gemini-2.5-flash-tts`, `gemini-2.5-pro-tts` | Which Gemini TTS model to use. gemini-2.5-flash-tts: low latency, cost-efficient for everyday applications (recommended). gemini-2.5-pro-tts: highest quality, best for structured workflows like podcasts, audiobooks, and customer support. | | `voice` | string | optional | `Achernar`, `Achird`, `Algenib`, `Algieba`, `Alnilam`, `Aoede`, `Autonoe`, `Callirrhoe`, `Charon`, `Despina`, `Enceladus`, `Erinome`, `Fenrir`, `Gacrux`, `Iapetus`, `Kore`, `Laomedeia`, `Leda`, `Orus`, `Pulcherrima`, `Puck`, `Rasalgethi`, `Sadachbia`, `Sadaltager`, `Schedar`, `Sulafat`, `Umbriel`, `Vindemiatrix`, `Zephyr`, `Zubenelgenubi` | Voice preset for single-speaker synthesis. 30 distinct voices are available. Ignored when speakers is set. Popular choices: Kore (strong, firm female), Puck (upbeat, lively male), Charon (calm, professional male), Zephyr (bright, clear female), Aoede (warm, melodic female). | | `language_code` | string | optional | Any | Language for multilingual synthesis. When set, steers the model to speak in the specified language. Supports 24 GA languages and 60+ Preview languages. If not set, the model auto-detects the language from the text. | | `temperature` | float | optional | Any | Controls the randomness of the speech output. Higher values produce more creative and varied delivery, while lower values make the output more predictable and focused. | | `style_instructions` | string | optional | Any | Optional style and delivery instructions prepended to the prompt. Controls expressiveness, accent, pace, tone, and emotional expression using natural language. Use this to separate style control from the text content. Examples: 'Speak warmly and slowly', 'Read this as a dramatic newscast', 'Use a British accent with a cheerful tone', 'Whisper mysteriously'. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/google/gemini-tts) - [API Reference](https://app.runflow.io/models/google/gemini-tts?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Nano Banana 2 Pricing: **$0.08/image**. Endpoint: `POST /v1/models/google/nano-banana-2/runs`. Nano Banana 2 is Google's new state-of-the-art fast image generation and editing model ## Overview - **Endpoint**: https://api.runflow.io/v1/models/google/nano-banana-2/runs - **Model ID**: google/nano-banana-2 - **Provider**: Google - **License**: commercial - **Last Updated**: 2026-04-13 ## Pricing - **Base price**: $0.08/image - **Note**: Per image (1K default) # Nano Banana 2 API **Endpoint:** `POST /v1/models/google/nano-banana-2/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/google/nano-banana-2/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A bioluminescent treehouse hotel suspended between ancient redwood trees at twilight. Three geodesic glass pods connected by vine-wrapped rope bridges, each pod glowing turquoise from within. Orchids and phosphorescent mushrooms grow on the bark. A narrow wooden staircase spirals up the trunk. Mist drifts through the canopy." }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/google/nano-banana-2/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A bioluminescent treehouse hotel suspended between ancient redwood trees at twilight. Three geodesic glass pods connected by vine-wrapped rope bridges, each pod glowing turquoise from within. Orchids and phosphorescent mushrooms grow on the bark. A narrow wooden staircase spirals up the trunk. Mist drifts through the canopy." }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/google/nano-banana-2/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A bioluminescent treehouse hotel suspended between ancient redwood trees at twilight. Three geodesic glass pods connected by vine-wrapped rope bridges, each pod glowing turquoise from within. Orchids and phosphorescent mushrooms grow on the bark. A narrow wooden staircase spirals up the trunk. Mist drifts through the canopy." }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the image. 3–50,000 characters. | | `resolution` | string | optional | `0.5K`, `1K`, `2K`, `4K` | Output resolution. Options: "0.5K", "1K", "2K", "4K". Default: "1K". Price varies by tier. | | `aspect_ratio` | string | optional | `auto`, `21:9`, `16:9`, `3:2`, `4:3`, `5:4`, `1:1`, `4:5`, `3:4`, `2:3`, `9:16`, `4:1`, `1:4`, `8:1`, `1:8` | Image aspect ratio. Options: "21:9", "16:9", "3:2", "4:3", "5:4", "1:1", "4:5", "3:4", "2:3", "9:16", "4:1", "1:4", "8:1", "1:8". Use "auto" to let the model decide. Default: "auto". | | `enable_web_search` | boolean | optional | Any | Enable web search to ground generation in current information. +$0.015/image. Default: false. | | `thinking_level` | string | optional | `minimal`, `high` | Enable model thinking. Options: "minimal", "high". Adds +$0.002/image at high level. Omit to disable. | | `num_images` | integer | optional | Any | Number of images to generate. Range: 1–4. Default: 1. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | Output format. Options: "jpeg", "png", "webp". Default: "png". | | `safety_tolerance` | string | optional | Any | Content moderation level. 1 = most strict, 6 = least strict. Default: 4. | | `seed` | integer | optional | Any | Random seed for reproducible outputs. | | `limit_generations` | boolean | optional | Any | Limit to 1 image per prompt round regardless of prompt instructions. Default: true. | | `sync_mode` | boolean | optional | Any | Return media as data URI without storing in history. Default: false. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/google/nano-banana-2) - [API Reference](https://app.runflow.io/models/google/nano-banana-2?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Nano Banana 2 - Edit Pricing: **$0.08/image**. Endpoint: `POST /v1/models/google/nano-banana-2/edit/runs`. Nano Banana 2 is Google's new state-of-the-art image generation and editing model ## Overview - **Endpoint**: https://api.runflow.io/v1/models/google/nano-banana-2/edit/runs - **Model ID**: google/nano-banana-2/edit - **Provider**: Google - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.08/image - **Note**: Usage-based, per image # Nano Banana 2 - Edit API **Endpoint:** `POST /v1/models/google/nano-banana-2/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/google/nano-banana-2/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Transform this stained glass window into a massive underwater aquarium...", "image_urls": [ "https://public.runflow.io/images/models/google/nano-banana-pro/example-output-0.webp" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/google/nano-banana-2/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Transform this stained glass window into a massive underwater aquarium...", "image_urls": [ "https://public.runflow.io/images/models/google/nano-banana-pro/example-output-0.webp" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/google/nano-banana-2/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Transform this stained glass window into a massive underwater aquarium...", "image_urls": [ "https://public.runflow.io/images/models/google/nano-banana-pro/example-output-0.webp" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The prompt for image editing. | | `aspect_ratio` | string | optional | Any | The aspect ratio of the generated image. Supports extreme ratios: 4:1, 1:4, 8:1, 1:8. | | `enable_web_search` | boolean | optional | Any | Enable web search for the image generation task. This will allow the model to use the latest information from the web to generate the image. | | `resolution` | string | optional | `0.5K`, `1K`, `2K`, `4K` | The resolution of the image to generate. | | `num_images` | integer | optional | Any | The number of images to generate. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | The format of the generated image. | | `thinking_level` | string | optional | Any | When set, enables model thinking with the given level ('minimal' or 'high') and includes thoughts in the generation. Omit to disable. | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | | `safety_tolerance` | string | optional | `1`, `2`, `3`, `4`, `5`, `6` | The safety tolerance level for content moderation. 1 is the most strict (blocks most content), 6 is the least strict. | | `seed` | integer | optional | Any | The seed for the random number generator. | | `image_urls` | image_list | required | Any | The URLs of the images to use for image-to-image generation or image editing. | | `limit_generations` | boolean | optional | Any | Experimental parameter to limit the number of generations from each round of prompting to 1. Set to `True` to to disregard any instructions in the prompt regarding the number of images to generate and ignore any intermediate images generated by the model. This may affect generation quality. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/google/nano-banana-2/edit) - [API Reference](https://app.runflow.io/models/google/nano-banana-2/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Nano Banana Pro Pricing: **$0.15/image**. Endpoint: `POST /v1/models/google/nano-banana-pro/runs`. Nano Banana Pro is Google's new state-of-the-art image generation and editing model ## Overview - **Endpoint**: https://api.runflow.io/v1/models/google/nano-banana-pro/runs - **Model ID**: google/nano-banana-pro - **Provider**: Google - **License**: commercial - **Last Updated**: 2026-04-13 ## Pricing - **Base price**: $0.15/image - **Note**: Usage-based, per image # Nano Banana Pro API **Endpoint:** `POST /v1/models/google/nano-banana-pro/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/google/nano-banana-pro/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A cathedral made entirely of stained glass, viewed from inside looking up at the rose window ceiling. Sunlight streams through panels depicting vineyard scenes with grapes and golden wheat. The floor reflects rainbow light patterns. Photorealistic, shot on medium format camera, golden hour" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/google/nano-banana-pro/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A cathedral made entirely of stained glass, viewed from inside looking up at the rose window ceiling. Sunlight streams through panels depicting vineyard scenes with grapes and golden wheat. The floor reflects rainbow light patterns. Photorealistic, shot on medium format camera, golden hour" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/google/nano-banana-pro/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A cathedral made entirely of stained glass, viewed from inside looking up at the rose window ceiling. Sunlight streams through panels depicting vineyard scenes with grapes and golden wheat. The floor reflects rainbow light patterns. Photorealistic, shot on medium format camera, golden hour" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The text prompt to generate an image from. | | `aspect_ratio` | string | optional | Any | The aspect ratio of the generated image. | | `enable_web_search` | boolean | optional | Any | Enable web search for the image generation task. This will allow the model to use the latest information from the web to generate the image. | | `resolution` | string | optional | `1K`, `2K`, `4K` | The resolution of the image to generate. | | `num_images` | integer | optional | Any | The number of images to generate. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | The format of the generated image. | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | | `safety_tolerance` | string | optional | `1`, `2`, `3`, `4`, `5`, `6` | The safety tolerance level for content moderation. 1 is the most strict (blocks most content), 6 is the least strict. | | `seed` | integer | optional | Any | The seed for the random number generator. | | `limit_generations` | boolean | optional | Any | Experimental parameter to limit the number of generations from each round of prompting to 1. Set to `True` to to disregard any instructions in the prompt regarding the number of images to generate. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/google/nano-banana-pro) - [API Reference](https://app.runflow.io/models/google/nano-banana-pro?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Nano Banana Pro - Edit Pricing: **$0.15/image**. Endpoint: `POST /v1/models/google/nano-banana-pro/edit/runs`. Nano Banana Pro is Google's new state-of-the-art image generation and editing model ## Overview - **Endpoint**: https://api.runflow.io/v1/models/google/nano-banana-pro/edit/runs - **Model ID**: google/nano-banana-pro/edit - **Provider**: Google - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.15/image - **Note**: Usage-based, per image # Nano Banana Pro - Edit API **Endpoint:** `POST /v1/models/google/nano-banana-pro/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/google/nano-banana-pro/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Convert to Studio Ghibli anime style: soft watercolor textures, warm pastel lighting, the woman becomes an anime character with large expressive eyes, the monitors show hand-drawn code, the lamp glows with magical warmth, keep the same pose and composition", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/google/nano-banana-pro/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Convert to Studio Ghibli anime style: soft watercolor textures, warm pastel lighting, the woman becomes an anime character with large expressive eyes, the monitors show hand-drawn code, the lamp glows with magical warmth, keep the same pose and composition", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/google/nano-banana-pro/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Convert to Studio Ghibli anime style: soft watercolor textures, warm pastel lighting, the woman becomes an anime character with large expressive eyes, the monitors show hand-drawn code, the lamp glows with magical warmth, keep the same pose and composition", "image_urls": [ "https://public.runflow.io/images/models/_shared/base-engineer.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The prompt for image editing. | | `aspect_ratio` | string | optional | Any | The aspect ratio of the generated image. | | `enable_web_search` | boolean | optional | Any | Enable web search for the image generation task. This will allow the model to use the latest information from the web to generate the image. | | `resolution` | string | optional | `1K`, `2K`, `4K` | The resolution of the image to generate. | | `num_images` | integer | optional | Any | The number of images to generate. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | The format of the generated image. | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | | `safety_tolerance` | string | optional | `1`, `2`, `3`, `4`, `5`, `6` | The safety tolerance level for content moderation. 1 is the most strict (blocks most content), 6 is the least strict. | | `seed` | integer | optional | Any | The seed for the random number generator. | | `image_urls` | image_list | required | Any | The URLs of the images to use for image-to-image generation or image editing. | | `limit_generations` | boolean | optional | Any | Experimental parameter to limit the number of generations from each round of prompting to 1. Set to `True` to to disregard any instructions in the prompt regarding the number of images to generate. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/google/nano-banana-pro/edit) - [API Reference](https://app.runflow.io/models/google/nano-banana-pro/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # HeyGen Video Agent V3 Pricing: **$0.034/second**. Endpoint: `POST /v1/models/heygen/v3/video-agent/runs`. Generate videos with a single prompt. Describe what you want in plain text, and the agent handles avatar selection, scripting, scene composition - all in one. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/heygen/v3/video-agent/runs - **Model ID**: heygen/v3/video-agent - **Provider**: HeyGen - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.034/second - **Note**: Usage-based, per second of generated video # HeyGen Video Agent V3 API **Endpoint:** `POST /v1/models/heygen/v3/video-agent/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/heygen/v3/video-agent/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Create a 20-second professional product announcement video from a CTO introducing a new AI video platform to prospective customers. Modern office backdrop, warm professional lighting, confident tone, medium shot, minimal graphics. End with call-to-action." }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/heygen/v3/video-agent/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Create a 20-second professional product announcement video from a CTO introducing a new AI video platform to prospective customers. Modern office backdrop, warm professional lighting, confident tone, medium shot, minimal graphics. End with call-to-action." }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/heygen/v3/video-agent/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Create a 20-second professional product announcement video from a CTO introducing a new AI video platform to prospective customers. Modern office backdrop, warm professional lighting, confident tone, medium shot, minimal graphics. End with call-to-action." }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Natural language prompt describing the video to generate. The agent handles avatar selection, script, and production automatically. | | `orientation` | string | optional | `landscape`, `portrait` | Video orientation. Auto-detected from the prompt if omitted. | | `avatar` | string | optional | Any | Avatar to use in the video. Set to 'auto' to let the agent auto-select based on the prompt. | | `file_urls` | file_list | optional | Any | URLs of files to include as assets in the video (images, videos, audio, PDFs). Maximum 20 files. | | `incognito_mode` | boolean | optional | Any | When enabled, disables the agent's memory functions. | | `style_id` | string | optional | Any | Style template ID from the /v3/video-agent/styles endpoint. | | `voice` | string | optional | Any | Voice for narration. Set to 'auto' to let the agent auto-select. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. | | `seed` | json | Seed used for generation, or null. | | `timing` | json | Provider timing info, or null. | | `nsfw_detected` | json | NSFW flag, or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/heygen/v3/video-agent) - [API Reference](https://app.runflow.io/models/heygen/v3/video-agent?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Ideogram v3 Pricing: **$0.06/image**. Endpoint: `POST /v1/models/ideogram/v3/runs`. Generate high-quality images, posters, and logos with Ideogram V3. Features exceptional typography handling and realistic outputs optimized for commercial and creative use. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/ideogram/v3/runs - **Model ID**: ideogram/v3 - **Provider**: Ideogram - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.06/image - **Note**: Balanced (default) # Ideogram v3 API **Endpoint:** `POST /v1/models/ideogram/v3/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/ideogram/v3/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Product packaging design for an artisan coffee bag, front label featuring the brand name 'ALTITUDE COFFEE CO.' in bold vintage slab-serif at the top, a detailed woodcut-style illustration of a mountain coffee plantation in the center, origin text 'SINGLE ORIGIN - COLOMBIAN SUPREMO' in smaller caps below the illustration, tasting notes 'NOTES: DARK CHOCOLATE, CARAMEL, TOASTED WALNUT' in elegant thin serif at the bottom, roast level indicator showing 'MEDIUM-DARK ROAST', net weight '340g / 12oz' in the corner, kraft paper texture background with deep burgundy and gold ink, premium specialty coffee aesthetic", "image_size": "portrait_4_3", "expand_prompt": False, "negative_prompt": "blurry text, misspelled, low quality, generic", "rendering_speed": "QUALITY" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/ideogram/v3/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Product packaging design for an artisan coffee bag, front label featuring the brand name 'ALTITUDE COFFEE CO.' in bold vintage slab-serif at the top, a detailed woodcut-style illustration of a mountain coffee plantation in the center, origin text 'SINGLE ORIGIN - COLOMBIAN SUPREMO' in smaller caps below the illustration, tasting notes 'NOTES: DARK CHOCOLATE, CARAMEL, TOASTED WALNUT' in elegant thin serif at the bottom, roast level indicator showing 'MEDIUM-DARK ROAST', net weight '340g / 12oz' in the corner, kraft paper texture background with deep burgundy and gold ink, premium specialty coffee aesthetic", "image_size": "portrait_4_3", "expand_prompt": false, "negative_prompt": "blurry text, misspelled, low quality, generic", "rendering_speed": "QUALITY" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/ideogram/v3/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Product packaging design for an artisan coffee bag, front label featuring the brand name 'ALTITUDE COFFEE CO.' in bold vintage slab-serif at the top, a detailed woodcut-style illustration of a mountain coffee plantation in the center, origin text 'SINGLE ORIGIN - COLOMBIAN SUPREMO' in smaller caps below the illustration, tasting notes 'NOTES: DARK CHOCOLATE, CARAMEL, TOASTED WALNUT' in elegant thin serif at the bottom, roast level indicator showing 'MEDIUM-DARK ROAST', net weight '340g / 12oz' in the corner, kraft paper texture background with deep burgundy and gold ink, premium specialty coffee aesthetic", "image_size": "portrait_4_3", "expand_prompt": false, "negative_prompt": "blurry text, misspelled, low quality, generic", "rendering_speed": "QUALITY" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | | | `num_images` | integer | optional | Any | Number of images to generate. | | `image_size` | string | optional | Any | The resolution of the generated image | | `style` | string | optional | Any | The style type to generate with. Cannot be used with style_codes. | | `style_preset` | string | optional | Any | Style preset for generation. The chosen style preset will guide the generation. | | `expand_prompt` | boolean | optional | Any | Determine if MagicPrompt should be used in generating the request or not. | | `rendering_speed` | string | optional | `TURBO`, `BALANCED`, `QUALITY` | The rendering speed to use. | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | | `color_palette` | json | optional | Any | A color palette for generation, must EITHER be specified via one of the presets (name) or explicitly via hexadecimal representations of the color with optional weights (members) | | `style_codes` | json | optional | Any | A list of 8 character hexadecimal codes representing the style of the image. Cannot be used in conjunction with style_reference_images or style | | `seed` | integer | optional | Any | Seed for the random number generator | | `image_urls` | image_list | optional | Any | A set of images to use as style references (maximum total size 10MB across all style references). The images should be in JPEG, PNG or WebP format | | `negative_prompt` | string | optional | Any | Description of what to exclude from an image. Descriptions in the prompt take precedence to descriptions in the negative prompt. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `seed` | json | Seed used for the random number generator | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/ideogram/v3) - [API Reference](https://app.runflow.io/models/ideogram/v3?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Models 51 active models across 6 categories. Each model is one HTTP endpoint: `POST /v1/models/{provider}/{slug}/runs`. Auth: `Authorization: Bearer $RUNFLOW_API_KEY`. ## Image to image | Model | Provider | Pricing | |---|---|---| | [Qwen Image Edit 2511](/models/alibaba/qwen-image-edit-2511) | Alibaba | `$0.03/megapixel` | | [Wan 2.7 Pro Edit](/models/alibaba/wan/v2.7/pro/edit) | Alibaba | `$0.075/image` | | [FLUX.1 [dev] Edit](/models/black-forest-labs/flux-1-dev/edit) | Black Forest Labs | `$0.025/image` | | [FLUX.2 [klein] 4B Edit](/models/black-forest-labs/flux-2-klein-4b/edit) | Black Forest Labs | `$0.005/image` | | [FLUX.2 [klein] 9B - Edit](/models/black-forest-labs/flux-2-klein-9b/edit) | Black Forest Labs | `$0.011/megapixel` | | [FLUX Kontext](/models/black-forest-labs/flux-pro/kontext) | Black Forest Labs | `$0.04/image` | | [Bria Background Remove](/models/bria/background/remove) | Bria AI | `$0.018/request` | | [Nano Banana 2](/models/google/nano-banana-2) | Google | `$0.08/image` | | [Nano Banana 2 - Edit](/models/google/nano-banana-2/edit) | Google | `$0.08/image` | | [Nano Banana Pro](/models/google/nano-banana-pro) | Google | `$0.15/image` | | [Nano Banana Pro - Edit](/models/google/nano-banana-pro/edit) | Google | `$0.15/image` | | [SAM 3](/models/meta/sam-3/image) | Meta | `$0.005/image` | | [GPT Image 1.5](/models/openai/gpt-image-1.5) | OpenAI | `$0.133/image` | | [GPT Image 2 Edit](/models/openai/gpt-image-2/edit) | OpenAI | `$0.133/image` | | [Reve Edit](/models/reve/edit) | Reve | `$0.04/image` | | [Background Color](/models/runflow/background-color) | Runflow | `$0.12/request` | | [Background Removal](/models/runflow/background-removal) | Runflow | `$0.045/request` | | [Background Replace](/models/runflow/background-replace) | Runflow | `$0.35/request` | | [Eye Color](/models/runflow/eye-color) | Runflow | `$0.29/request` | | [Logo Inpaint](/models/runflow/logo-inpaint) | Runflow | `$0.2/request` | | [Model Removal](/models/runflow/model-removal) | Runflow | `$0.27/request` | | [Multi Angles](/models/runflow/multi-angles) | Runflow | `$0.2/request` | | [Object Removal](/models/runflow/object-removal) | Runflow | `$0.48/request` | | [Object Removal (Prompt)](/models/runflow/object-removal/prompt) | Runflow | `$0.48/request` | | [Outpaint (Expand)](/models/runflow/outpaint) | Runflow | `$0.55/request` | | [Outpaint (Aspect Ratio)](/models/runflow/outpaint/aspect-ratio) | Runflow | `$0.55/request` | | [Product Isolation](/models/runflow/product-isolation) | Runflow | `$0.27/request` | | [Reference Inpaint](/models/runflow/reference-inpaint) | Runflow | `$0.55/request` | | [Skin Fix](/models/runflow/skin-fix) | Runflow | `$0.12/request` | | [Smart Resize](/models/runflow/smart-resize) | Runflow | `$0.55/request` | | [Smart Segmentation](/models/runflow/smart-segmentation) | Runflow | `$0.12/request` | | [Tag Removal](/models/runflow/tag-removal) | Runflow | `$0.14/request` | | [Topaz Upscale - Image](/models/topaz/upscale/image) | Topaz Labs | `$0.08/image` | ## Text to image | Model | Provider | Pricing | |---|---|---| | [FLUX 1.1 [pro]](/models/black-forest-labs/flux-1-1-pro) | Black Forest Labs | `$0.04/image` | | [FLUX 1.1 [pro] Ultra](/models/black-forest-labs/flux-1-1-pro-ultra) | Black Forest Labs | `$0.06/image` | | [FLUX.2 [klein] 4B](/models/black-forest-labs/flux-2-klein-4b) | Black Forest Labs | `$0.005/megapixel` | | [FLUX.2 [klein] 9B](/models/black-forest-labs/flux-2-klein-9b) | Black Forest Labs | `$0.006/megapixel` | | [SDXL-Lightning](/models/bytedance/sd-xl-lightning) | ByteDance | `$0.003/image` | | [Ideogram v3](/models/ideogram/v3) | Ideogram | `$0.06/image` | | [GPT Image 2](/models/openai/gpt-image-2) | OpenAI | `$0.133/image` | ## Image to video | Model | Provider | Pricing | |---|---|---| | [Happy Horse Image-to-Video](/models/alibaba/happy-horse/image-to-video) | Alibaba | `$0.14/second` | | [Happy Horse Reference-to-Video](/models/alibaba/happy-horse/reference-to-video) | Alibaba | `$0.14/second` | | [Wan 2.7 - Image to Video](/models/alibaba/wan/v2.7/image-to-video) | Alibaba | `$0.1/second` | | [Kling v3 Pro - Image to Video](/models/kuaishou/kling-video/v3/pro/image-to-video) | Kuaishou | `$0.112/second` | ## Text to video | Model | Provider | Pricing | |---|---|---| | [Happy Horse Text-to-Video](/models/alibaba/happy-horse/text-to-video) | Alibaba | `$0.14/second` | | [Wan 2.7 Text to Video](/models/alibaba/wan/v2.7/text-to-video) | Alibaba | `$0.1/second` | | [HeyGen Video Agent V3](/models/heygen/v3/video-agent) | HeyGen | `$0.034/second` | | [Kling v3 Pro - Text to Video](/models/kuaishou/kling-video/v3/pro/text-to-video) | Kuaishou | `$0.112/second` | ## Text to audio | Model | Provider | Pricing | |---|---|---| | [ElevenLabs TTS v3](/models/elevenlabs/tts/eleven-v3) | ElevenLabs | `$100/1m-tokens` | | [Gemini TTS](/models/google/gemini-tts) | Google | `$10/1m-tokens` | ## Video to video | Model | Provider | Pricing | |---|---|---| | [Happy Horse Video Edit](/models/alibaba/happy-horse/video-edit) | Alibaba | `$0.28/second` | --- # Kling v3 Pro - Image to Video Pricing: **$0.112/second**. Endpoint: `POST /v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs`. Kling 3.0 Pro: Top-tier image-to-video with cinematic visuals, fluid motion, and native audio generation, with custom element support. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs - **Model ID**: kuaishou/kling-video/v3/pro/image-to-video - **Provider**: Kuaishou - **License**: commercial - **Last Updated**: 2026-04-05 ## Pricing - **Base price**: $0.112/second - **Note**: Without audio # Kling v3 Pro - Image to Video API **Endpoint:** `POST /v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Gentle head turn with a warm smile, hair blowing slightly in a soft breeze, cinematic portrait", "start_image_url": "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Gentle head turn with a warm smile, hair blowing slightly in a soft breeze, cinematic portrait", "start_image_url": "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/image-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Gentle head turn with a warm smile, hair blowing slightly in a soft breeze, cinematic portrait", "start_image_url": "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | optional | Any | Text prompt for video generation. Either prompt or multi_prompt must be provided, but not both. | | `elements` | json | optional | Any | Elements (characters/objects) to include in the video. Each example can either be an image set (frontal + reference images) or a video. Reference in prompt as @Element1, @Element2, etc. | | `multi_prompt` | json | optional | See nested fields below | List of prompts for multi-shot video generation. If provided, divides the video into multiple shots. | | `generate_audio` | boolean | optional | Any | Whether to generate native audio for the video. Supports Chinese and English voice output. Other languages are automatically translated to English. For English speech, use lowercase letters; for acronyms or proper nouns, use uppercase. | | `duration` | string | optional | `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15` | The duration of the generated video in seconds | | `shot_type` | string | optional | Any | The type of multi-shot video generation. Required when multi_prompt is provided. | | `start_image_url` | image | required | Any | URL of the image to be used for the video | | `end_image_url` | image | optional | Any | URL of the image to be used for the end of the video | | `negative_prompt` | string | optional | Any | | | `cfg_scale` | float | optional | Any | The CFG (Classifier Free Guidance) scale is a measure of how close you want the model to stick to your prompt. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/kuaishou/kling-video/v3/pro/image-to-video) - [API Reference](https://app.runflow.io/models/kuaishou/kling-video/v3/pro/image-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Kling v3 Pro - Text to Video Pricing: **$0.112/second**. Endpoint: `POST /v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs`. Kling 3.0 Pro: Top-tier text-to-video with cinematic visuals, fluid motion, and native audio generation, with multi-shot support. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs - **Model ID**: kuaishou/kling-video/v3/pro/text-to-video - **Provider**: Kuaishou - **License**: commercial - **Last Updated**: 2026-04-06 ## Pricing - **Base price**: $0.112/second - **Note**: Without audio # Kling v3 Pro - Text to Video API **Endpoint:** `POST /v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Time-lapse of a flower blooming in a sunlit garden, macro lens, smooth motion, warm tones" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Time-lapse of a flower blooming in a sunlit garden, macro lens, smooth motion, warm tones" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/kuaishou/kling-video/v3/pro/text-to-video/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Time-lapse of a flower blooming in a sunlit garden, macro lens, smooth motion, warm tones" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | optional | Any | Text prompt for video generation. Either prompt or multi_prompt must be provided, but not both. | | `aspect_ratio` | string | optional | `16:9`, `9:16`, `1:1` | The aspect ratio of the generated video frame | | `generate_audio` | boolean | optional | Any | Whether to generate native audio for the video. Supports Chinese and English voice output. Other languages are automatically translated to English. For English speech, use lowercase letters; for acronyms or proper nouns, use uppercase. | | `multi_prompt` | json | optional | See nested fields below | List of prompts for multi-shot video generation. If provided, overrides the single prompt and divides the video into multiple shots with specified prompts and durations. | | `duration` | string | optional | `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15` | The duration of the generated video in seconds | | `shot_type` | string | optional | `customize`, `intelligent` | The type of multi-shot video generation | | `negative_prompt` | string | optional | Any | | | `cfg_scale` | float | optional | Any | The CFG (Classifier Free Guidance) scale is a measure of how close you want the model to stick to your prompt. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/kuaishou/kling-video/v3/pro/text-to-video) - [API Reference](https://app.runflow.io/models/kuaishou/kling-video/v3/pro/text-to-video?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # SAM 3 Pricing: **$0.005/image**. Endpoint: `POST /v1/models/meta/sam-3/image/runs`. SAM 3 is a unified foundation model for promptable segmentation in images and videos. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/meta/sam-3/image/runs - **Model ID**: meta/sam-3/image - **Provider**: Meta - **License**: commercial - **Last Updated**: 2026-04-05 ## Pricing - **Base price**: $0.005/image - **Note**: Usage-based, per image # SAM 3 API **Endpoint:** `POST /v1/models/meta/sam-3/image/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/meta/sam-3/image/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "keyboard", "image_url": "https://v3b.fal.media/files/b/0a92a995/fV5oYdcCV3NAmgWJISpIP_tmpdddcks4v.png", "apply_mask": False, "return_multiple_masks": True }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/meta/sam-3/image/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "keyboard", "image_url": "https://v3b.fal.media/files/b/0a92a995/fV5oYdcCV3NAmgWJISpIP_tmpdddcks4v.png", "apply_mask": false, "return_multiple_masks": true }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/meta/sam-3/image/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "keyboard", "image_url": "https://v3b.fal.media/files/b/0a92a995/fV5oYdcCV3NAmgWJISpIP_tmpdddcks4v.png", "apply_mask": false, "return_multiple_masks": true }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | optional | Any | Text prompt for segmentation | | `include_boxes` | boolean | optional | Any | Whether to include bounding boxes for each mask (when available). | | `include_scores` | boolean | optional | Any | Whether to include mask confidence scores. | | `return_multiple_masks` | boolean | optional | Any | If True, upload and return multiple generated masks as defined by `max_masks`. | | `image_url` | image | required | Any | URL of the image to be segmented | | `sync_mode` | boolean | optional | Any | If True, the media will be returned as a data URI. | | `point_prompts` | json | optional | Any | List of point prompts | | `output_format` | string | optional | `jpeg`, `png`, `webp` | The format of the generated image. | | `max_masks` | integer | optional | Any | Maximum number of masks to return when `return_multiple_masks` is enabled. | | `box_prompts` | json | optional | Any | Box prompt coordinates (x_min, y_min, x_max, y_max). Multiple boxes supported - use object_id to group boxes for the same object or leave empty for separate objects. | | `apply_mask` | boolean | optional | Any | Apply the mask on the image. | | `text_prompt` | string | optional | Any | [DEPRECATED] Use 'prompt' instead. Kept for backward compatibility. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `model_data` | json | Model-specific extra fields (prompt, description, actual_prompt, masks, etc.) keyed by field name. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/meta/sam-3/image) - [API Reference](https://app.runflow.io/models/meta/sam-3/image?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # GPT Image 1.5 Pricing: **$0.133/image**. Endpoint: `POST /v1/models/openai/gpt-image-1.5/runs`. GPT Image 1.5 generates high-fidelity images with strong prompt adherence, preserving composition, lighting, and fine-grained detail. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/openai/gpt-image-1.5/runs - **Model ID**: openai/gpt-image-1.5 - **Provider**: OpenAI - **License**: commercial - **Last Updated**: 2026-04-13 ## Pricing - **Base price**: $0.133/image - **Note**: High quality (default) # GPT Image 1.5 API **Endpoint:** `POST /v1/models/openai/gpt-image-1.5/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/openai/gpt-image-1.5/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "An underwater coral reef tea house at the bottom of a crystal-clear tropical ocean. Bamboo replaced by swaying kelp and sea fans, fish swim where birds would fly, jellyfish provide the lantern light, a sea turtle approaches the entrance. Warm bioluminescent glow, caustic light patterns on the sandy floor." }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/openai/gpt-image-1.5/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "An underwater coral reef tea house at the bottom of a crystal-clear tropical ocean. Bamboo replaced by swaying kelp and sea fans, fish swim where birds would fly, jellyfish provide the lantern light, a sea turtle approaches the entrance. Warm bioluminescent glow, caustic light patterns on the sandy floor." }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/openai/gpt-image-1.5/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "An underwater coral reef tea house at the bottom of a crystal-clear tropical ocean. Bamboo replaced by swaying kelp and sea fans, fish swim where birds would fly, jellyfish provide the lantern light, a sea turtle approaches the entrance. Warm bioluminescent glow, caustic light patterns on the sandy floor." }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Text prompt describing the image to generate. 2–32,000 characters. | | `quality` | string | optional | `low`, `medium`, `high` | Output quality tier. Options: "low", "medium", "high". Default: "high". Affects price. | | `image_size` | string | optional | `1024x1024`, `1536x1024`, `1024x1536` | Image dimensions. Options: "1024x1024", "1536x1024", "1024x1536", "auto". Default: "1024x1024". Use "auto" to let the model pick the best size. | | `background` | string | optional | `auto`, `transparent`, `opaque` | Background type. Options: "auto", "transparent", "opaque". Default: "auto". | | `num_images` | integer | optional | Any | Number of images to generate. Range: 1–4. Default: 1. | | `output_format` | string | optional | `jpeg`, `png`, `webp` | Output format. Options: "jpeg", "png", "webp". Default: "png". | | `sync_mode` | boolean | optional | Any | Return media as data URI without storing in history. Default: false. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/openai/gpt-image-1.5) - [API Reference](https://app.runflow.io/models/openai/gpt-image-1.5?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # GPT Image 2 Pricing: **$0.133/image**. Endpoint: `POST /v1/models/openai/gpt-image-2/runs`. GPT Image 2.0, OpenAI's latest image model, is capable of creating extremely detailed images with fine typography. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/openai/gpt-image-2/runs - **Model ID**: openai/gpt-image-2 - **Provider**: OpenAI - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.133/image - **Note**: High quality (default) # GPT Image 2 API **Endpoint:** `POST /v1/models/openai/gpt-image-2/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/openai/gpt-image-2/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "A hand-drawn storefront sign reading 'CIRCUIT & GRAIN - specialty coffee' on polished brass, warm morning sunlight, shallow depth of field, the shop window visible behind with pastry trays, cinematic photograph" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/openai/gpt-image-2/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "A hand-drawn storefront sign reading 'CIRCUIT & GRAIN - specialty coffee' on polished brass, warm morning sunlight, shallow depth of field, the shop window visible behind with pastry trays, cinematic photograph" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/openai/gpt-image-2/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "A hand-drawn storefront sign reading 'CIRCUIT & GRAIN - specialty coffee' on polished brass, warm morning sunlight, shallow depth of field, the shop window visible behind with pastry trays, cinematic photograph" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The prompt for image generation | | `num_images` | integer | optional | Any | Number of images to generate | | `output_format` | string | optional | `jpeg`, `png`, `webp` | Output format for the images | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9` | The size of the generated image. Supports preset names or explicit {width, height}. Both dimensions must be multiples of 16, max edge 3840px, aspect ratio <= 3:1, total pixels between 655,360 and 8,294,400. | | `quality` | string | optional | `low`, `medium`, `high` | Quality for the generated image | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. | | `seed` | json | Seed used for generation, or null. | | `timing` | json | Provider timing info, or null. | | `nsfw_detected` | json | NSFW flag, or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/openai/gpt-image-2) - [API Reference](https://app.runflow.io/models/openai/gpt-image-2?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # GPT Image 2 Edit Pricing: **$0.133/image**. Endpoint: `POST /v1/models/openai/gpt-image-2/edit/runs`. GPT Image 2.0, OpenAI's latest image model, is capable of making fine-grained, detailed edits to images. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/openai/gpt-image-2/edit/runs - **Model ID**: openai/gpt-image-2/edit - **Provider**: OpenAI - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.133/image - **Note**: High quality (default) # GPT Image 2 Edit API **Endpoint:** `POST /v1/models/openai/gpt-image-2/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/openai/gpt-image-2/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Replace the background with a sunlit seaside cafe at golden hour, keep the subject's pose, outfit, and lighting on the face intact", "image_urls": [ "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" ] }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/openai/gpt-image-2/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Replace the background with a sunlit seaside cafe at golden hour, keep the subject's pose, outfit, and lighting on the face intact", "image_urls": [ "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" ] }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/openai/gpt-image-2/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Replace the background with a sunlit seaside cafe at golden hour, keep the subject's pose, outfit, and lighting on the face intact", "image_urls": [ "https://public.runflow.io/images/models/_base/portrait-woman-studio.png" ] }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The prompt for image generation | | `image_urls` | image_list | required | Any | The URLs of the images to use as a reference for the generation. | | `num_images` | integer | optional | Any | Number of images to generate | | `output_format` | string | optional | `jpeg`, `png`, `webp` | Output format for the images | | `image_size` | string | optional | `square_hd`, `square`, `portrait_4_3`, `portrait_16_9`, `landscape_4_3`, `landscape_16_9`, `auto` | The size of the generated image. Use 'auto' to infer from input images. | | `mask_url` | image | optional | Any | The URL of the mask image to use for the generation. This indicates what part of the image to edit. | | `quality` | string | optional | `low`, `medium`, `high` | Quality for the generated image | | `sync_mode` | boolean | optional | Any | If `True`, the media will be returned as a data URI and the output data won't be available in the request history. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array. | | `seed` | json | Seed used for generation, or null. | | `timing` | json | Provider timing info, or null. | | `nsfw_detected` | json | NSFW flag, or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/openai/gpt-image-2/edit) - [API Reference](https://app.runflow.io/models/openai/gpt-image-2/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Reve Edit Pricing: **$0.04/image**. Endpoint: `POST /v1/models/reve/edit/runs`. Reve's edit model lets you upload an existing image and then transform it via a text prompt. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/reve/edit/runs - **Model ID**: reve/edit - **Provider**: Reve - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.04/image - **Note**: Per image # Reve Edit API **Endpoint:** `POST /v1/models/reve/edit/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/reve/edit/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Move this entire scene to a rooftop terrace in Tokyo at sunset: the desk is now outdoors on a wooden deck, Tokyo Tower and neon skyscrapers visible in the background, cherry blossom petals floating in the air, warm golden hour light, string lights overhead", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/reve/edit/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Move this entire scene to a rooftop terrace in Tokyo at sunset: the desk is now outdoors on a wooden deck, Tokyo Tower and neon skyscrapers visible in the background, cherry blossom petals floating in the air, warm golden hour light, string lights overhead", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/reve/edit/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Move this entire scene to a rooftop terrace in Tokyo at sunset: the desk is now outdoors on a wooden deck, Tokyo Tower and neon skyscrapers visible in the background, cherry blossom petals floating in the air, warm golden hour light, string lights overhead", "image_url": "https://public.runflow.io/images/models/_shared/base-engineer.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | The text description of how to edit the provided image. | | `image_url` | image | required | Any | URL of the reference image to edit. Must be publicly accessible or base64 data URI. Supports PNG, JPEG, WebP, AVIF, and HEIF formats. | | `num_images` | integer | optional | Any | Number of images to generate | | `output_format` | string | optional | `png`, `jpeg`, `webp` | Output format for the generated image. | | `sync_mode` | boolean | optional | Any | If True, the media will be returned as a data URI and the output data won't be available in the request history. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/reve/edit) - [API Reference](https://app.runflow.io/models/reve/edit?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Background Color Pricing: **$0.12/request**. Endpoint: `POST /v1/models/runflow/background-color/runs`. Replace the background of a portrait or product shot with a flat color. Pick any RGB value; shading_percent baked at 30% for soft falloff. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/background-color/runs - **Model ID**: runflow/background-color - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.12/request - **Note**: Per image - background color replacement # Background Color API **Endpoint:** `POST /v1/models/runflow/background-color/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/background-color/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "color_red": 245, "image_url": "https://public.runflow.io/images/models/runflow/background-color/examples/1/before.webp", "color_blue": 200, "color_green": 220 }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/background-color/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "color_red": 245, "image_url": "https://public.runflow.io/images/models/runflow/background-color/examples/1/before.webp", "color_blue": 200, "color_green": 220 }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-color/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "color_red": 245, "image_url": "https://public.runflow.io/images/models/runflow/background-color/examples/1/before.webp", "color_blue": 200, "color_green": 220 }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. HTTPS required. | | `color_red` | integer | required | Any | Red channel 0–255. | | `color_green` | integer | required | Any | Green channel 0–255. | | `color_blue` | integer | required | Any | Blue channel 0–255. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/background-color) - [API Reference](https://app.runflow.io/models/runflow/background-color?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Background Removal Pricing: **$0.045/request**. Endpoint: `POST /v1/models/runflow/background-removal/runs`. Remove the background from an image and return a clean cutout with a transparent alpha channel. Built for e-commerce product shots, portraits, and batch cleanup pipelines. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/background-removal/runs - **Model ID**: runflow/background-removal - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.045/request - **Note**: Per image - background removal # Background Removal API **Endpoint:** `POST /v1/models/runflow/background-removal/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/background-removal/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/background-removal/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-removal/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. | | `config` | json | optional | See nested fields below | Optional per-image config (aspect_ratio). | ### `config` - nested fields Optional per-image config (aspect_ratio). | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `aspect_ratio` | string | optional | Suggested: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `2:3` | | ```json {} ``` ## Output schema | Field | Type | Description | | --- | --- | --- | | `image_urls` | image_list | Processed image URLs. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/background-removal) - [API Reference](https://app.runflow.io/models/runflow/background-removal?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Background Replace Pricing: **$0.35/request**. Endpoint: `POST /v1/models/runflow/background-replace/runs`. Swap the background of any product photo for a new scene defined by a reference image and a short prompt. The model preserves the foreground subject while matching camera angle and lighting to the new background. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/background-replace/runs - **Model ID**: runflow/background-replace - **Provider**: Runflow - **Last Updated**: 2026-05-06 ## Pricing - **Base price**: $0.35/request - **Note**: Per image - background replace # Background Replace API **Endpoint:** `POST /v1/models/runflow/background-replace/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/background-replace/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Replace the white area on Image 1 with Image 2 as the background. Keep the perfume bottle perfectly intact and match the soft window light from Image 2.", "zip_url": "https://public.runflow.io/images/models/runflow/background-replace/examples/01-perfume-bottle-marble-counter/inputs.zip", "file_image": "image.webp", "file_reference": "reference.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/background-replace/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Replace the white area on Image 1 with Image 2 as the background. Keep the perfume bottle perfectly intact and match the soft window light from Image 2.", "zip_url": "https://public.runflow.io/images/models/runflow/background-replace/examples/01-perfume-bottle-marble-counter/inputs.zip", "file_image": "image.webp", "file_reference": "reference.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Replace the white area on Image 1 with Image 2 as the background. Keep the perfume bottle perfectly intact and match the soft window light from Image 2.", "zip_url": "https://public.runflow.io/images/models/runflow/background-replace/examples/01-perfume-bottle-marble-counter/inputs.zip", "file_image": "image.webp", "file_reference": "reference.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `zip_url` | file | required | Any | URL of a zip archive containing the foreground photo and the background reference image. Set file_image and file_reference to the filenames inside the zip. | | `file_image` | string | required | Any | Filename of the foreground/product photo inside the zip (e.g. `image.png`). The model keeps this subject and replaces only the background. | | `file_reference` | string | required | Any | Filename of the background reference image inside the zip (e.g. `reference.png`). The model uses it as the new scene behind the foreground. | | `prompt` | string | optional | Any | Optional override for the scene description. Leave empty to use the default: replace the white area on Image 1 with Image 2 as the background, matching camera angle and lighting. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `image_urls` | image_list | Background-replaced image URLs. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/background-replace) - [API Reference](https://app.runflow.io/models/runflow/background-replace?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Eye Color Pricing: **$0.29/request**. Endpoint: `POST /v1/models/runflow/eye-color/runs`. Recolor the irises in a portrait. The customer describes the target eye color (e.g. "blue", "emerald green", "amber") - that drives the inpaint guidance. Optional RGB lets you tint the overlay precisely. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/eye-color/runs - **Model ID**: runflow/eye-color - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-03 ## Pricing - **Base price**: $0.29/request - **Note**: Per image - eye color change # Eye Color API **Endpoint:** `POST /v1/models/runflow/eye-color/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/eye-color/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "color_red": 50, "eye_color": "forest green", "image_url": "https://public.runflow.io/images/models/runflow/eye-color/examples/1/before.webp", "color_blue": 60, "color_green": 110 }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/eye-color/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "color_red": 50, "eye_color": "forest green", "image_url": "https://public.runflow.io/images/models/runflow/eye-color/examples/1/before.webp", "color_blue": 60, "color_green": 110 }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/eye-color/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "color_red": 50, "eye_color": "forest green", "image_url": "https://public.runflow.io/images/models/runflow/eye-color/examples/1/before.webp", "color_blue": 60, "color_green": 110 }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. HTTPS required. | | `eye_color` | string | required | Any | Target eye color, e.g. "blue", "emerald green", "amber", "deep brown". | | `color_red` | integer | required | Any | Red channel 0–255. | | `color_green` | integer | required | Any | Green channel 0–255. | | `color_blue` | integer | required | Any | Blue channel 0–255. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/eye-color) - [API Reference](https://app.runflow.io/models/runflow/eye-color?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Logo Inpaint Pricing: **$0.2/request**. Endpoint: `POST /v1/models/runflow/logo-inpaint/runs`. Place a logo onto a target image, with optional mask to constrain placement. When the mask is omitted, the workflow picks a placement automatically. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/logo-inpaint/runs - **Model ID**: runflow/logo-inpaint - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-21 ## Pricing - **Base price**: $0.2/request - **Note**: Per image: logo inpaint (v2, 2026-05-21) # Logo Inpaint API **Endpoint:** `POST /v1/models/runflow/logo-inpaint/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/logo-inpaint/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "Apply the logo from Image 2 onto the masked area of the garment in Image 1, matching the fabric texture, surface curvature, lighting, and color tone of the garment. The logo should look professionally printed or embroidered into the surface, not pasted on top.", "logo_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/logo.webp", "mask_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/mask.png", "image_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/logo-inpaint/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Apply the logo from Image 2 onto the masked area of the garment in Image 1, matching the fabric texture, surface curvature, lighting, and color tone of the garment. The logo should look professionally printed or embroidered into the surface, not pasted on top.", "logo_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/logo.webp", "mask_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/mask.png", "image_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/logo-inpaint/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "Apply the logo from Image 2 onto the masked area of the garment in Image 1, matching the fabric texture, surface curvature, lighting, and color tone of the garment. The logo should look professionally printed or embroidered into the surface, not pasted on top.", "logo_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/logo.webp", "mask_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/mask.png", "image_url": "https://public.runflow.io/images/models/runflow/logo-inpaint/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the target image. HTTPS required. | | `logo_url` | image | required | Any | URL of the logo to place. PNG with transparency preferred. | | `mask_url` | image | required | Any | URL of a black-and-white mask. White pixels mark the placement zone; black keeps the original. | | `prompt` | string | optional | Any | Optional placement instruction (e.g. 'top-right, 20% width'). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items with url, type, width, height, content_type. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/logo-inpaint) - [API Reference](https://app.runflow.io/models/runflow/logo-inpaint?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Model Removal Pricing: **$0.27/request**. Endpoint: `POST /v1/models/runflow/model-removal/runs`. Remove the human model from a garment or apparel photo and recover a clean flat-lay or ghost-mannequin view. Designed for fashion catalogs and PDP hero shots. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/model-removal/runs - **Model ID**: runflow/model-removal - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.27/request - **Note**: Per image - model removal # Model Removal API **Endpoint:** `POST /v1/models/runflow/model-removal/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/model-removal/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/model-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/model-removal/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/model-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/model-removal/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/model-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. | | `config` | json | optional | See nested fields below | Optional per-image config (aspect_ratio). | ### `config` - nested fields Optional per-image config (aspect_ratio). | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `aspect_ratio` | string | optional | Suggested: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `2:3` | | ```json {} ``` ## Output schema | Field | Type | Description | | --- | --- | --- | | `image_urls` | image_list | Processed image URLs. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/model-removal) - [API Reference](https://app.runflow.io/models/runflow/model-removal?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Multi Angles Pricing: **$0.2/request**. Endpoint: `POST /v1/models/runflow/multi-angles/runs`. Re-render the same subject from a different camera angle. Pick a horizontal view, a vertical angle, and a shot distance: the model rotates the camera around the subject and re-renders the scene from that pose. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/multi-angles/runs - **Model ID**: runflow/multi-angles - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-28 ## Pricing - **Base price**: $0.2/request - **Note**: Per image. Multi-angle camera rotation. # Multi Angles API **Endpoint:** `POST /v1/models/runflow/multi-angles/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/multi-angles/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "shot": "medium shot", "view": "right side view", "angle": "eye-level shot", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/multi-angles/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "shot": "medium shot", "view": "right side view", "angle": "eye-level shot", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/multi-angles/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "shot": "medium shot", "view": "right side view", "angle": "eye-level shot", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | HTTPS URL of the source photo. A clear subject and even lighting work best. | | `view` | string | required | `front view`, `front-right quarter view`, `right side view`, `back-right quarter view`, `back view`, `back-left quarter view`, `left side view`, `front-left quarter view` | Where the camera sits horizontally around the subject. "front view" looks at the subject head-on; "right side view" rotates 90 degrees clockwise; "back view" looks from behind; and so on for the 8 cardinal + diagonal positions. | | `angle` | string | optional | `low-angle shot`, `eye-level shot`, `elevated shot`, `high-angle shot` | Vertical camera tilt. "low-angle shot" looks up from ground level (-30 degrees); "eye-level shot" matches the subject's height; "elevated shot" sits slightly above (30 degrees); "high-angle shot" looks down from above (60 degrees). | | `shot` | string | optional | `close-up`, `medium shot`, `wide shot` | Camera distance. "close-up" fills the frame with surface and texture detail; "medium shot" is a balanced standard framing; "wide shot" pulls back to include context and environment. | | `prompt` | string | optional | Any | Optional extra phrase appended to the camera pose to nudge the output (e.g. "keep the original lighting"). Leave blank for default behavior. | | `match_input_aspect` | string | optional | `true`, `false` | Keep the output at the source image's aspect ratio. Default. Set to "false" if you want to force a specific aspect_ratio. | | `aspect_ratio` | string | optional | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `21:9` | Output aspect ratio. Only takes effect when match_input_aspect is "false". | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/multi-angles) - [API Reference](https://app.runflow.io/models/runflow/multi-angles?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Object Removal Pricing: **$0.48/request**. Endpoint: `POST /v1/models/runflow/object-removal/runs`. Remove an unwanted object or region from a photo. Mark the area to remove with a black-and-white mask. The model rebuilds the background using surrounding context. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/object-removal/runs - **Model ID**: runflow/object-removal - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-05 ## Pricing - **Base price**: $0.48/request - **Note**: Per image: object removal # Object Removal API **Endpoint:** `POST /v1/models/runflow/object-removal/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/object-removal/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "zip_url": "https://public.runflow.io/images/models/runflow/object-removal/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/object-removal/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "zip_url": "https://public.runflow.io/images/models/runflow/object-removal/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/object-removal/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "zip_url": "https://public.runflow.io/images/models/runflow/object-removal/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `zip_url` | file | required | Any | URL of a zip archive containing the input photo and mask. Use file_input + file_mask to name the files inside. | | `file_input` | string | required | Any | Filename inside `zip_url` (e.g. `photo.png` if the zip contains `photo.png`). Frontend should set this to match the inner filename when composing the zip. | | `file_mask` | string | required | Any | Filename of the B&W mask inside the URL/zip (white = remove, black = keep). Same shape as `file_input` but for the mask file. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/object-removal) - [API Reference](https://app.runflow.io/models/runflow/object-removal?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Object Removal (Prompt) Pricing: **$0.48/request**. Endpoint: `POST /v1/models/runflow/object-removal/prompt/runs`. Remove requested objects from a photo using a short text instruction. Flux-fill restores the background without requiring a mask. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/object-removal/prompt/runs - **Model ID**: runflow/object-removal/prompt - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-07 ## Pricing - **Base price**: $0.48/request - **Note**: Per image - object removal (flux-fill) # Object Removal (Prompt) API **Endpoint:** `POST /v1/models/runflow/object-removal/prompt/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/object-removal/prompt/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "remove person on left", "image_url": "https://public.runflow.io/images/models/runflow/object-removal/prompt/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/object-removal/prompt/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "remove person on left", "image_url": "https://public.runflow.io/images/models/runflow/object-removal/prompt/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/object-removal/prompt/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "remove person on left", "image_url": "https://public.runflow.io/images/models/runflow/object-removal/prompt/examples/1/image.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | required | Any | Short removal instruction, up to 7 words. | | `image_url` | image | required | Any | URL of the input image to process. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/object-removal/prompt) - [API Reference](https://app.runflow.io/models/runflow/object-removal/prompt?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Outpaint (Expand) Pricing: **$0.55/request**. Endpoint: `POST /v1/models/runflow/outpaint/runs`. Expand the canvas of an image with AI-generated content that matches the existing scene. Specify percent expand on each side - required so the customer is intentional. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/outpaint/runs - **Model ID**: runflow/outpaint - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.55/request - **Note**: Per image - outpainting / canvas expand # Outpaint (Expand) API **Endpoint:** `POST /v1/models/runflow/outpaint/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/outpaint/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/examples/1/before.webp", "expand_top": 10, "expand_left": 70, "expand_right": 0, "expand_bottom": 30 }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/outpaint/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/examples/1/before.webp", "expand_top": 10, "expand_left": 70, "expand_right": 0, "expand_bottom": 30 }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/outpaint/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/examples/1/before.webp", "expand_top": 10, "expand_left": 70, "expand_right": 0, "expand_bottom": 30 }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. HTTPS required. | | `expand_top` | integer | required | Any | 0–80% expand on the top edge. | | `expand_bottom` | integer | required | Any | 0–80% expand on the bottom edge. | | `expand_left` | integer | required | Any | 0–80% expand on the left edge. | | `expand_right` | integer | required | Any | 0–80% expand on the right edge. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/outpaint) - [API Reference](https://app.runflow.io/models/runflow/outpaint?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Outpaint (Aspect Ratio) Pricing: **$0.55/request**. Endpoint: `POST /v1/models/runflow/outpaint/aspect-ratio/runs`. Expand the canvas of an image to a target aspect ratio (e.g. 16:9, 1:1, 9:16). The model decides where to add content - no need to specify per-side expand percentages. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/outpaint/aspect-ratio/runs - **Model ID**: runflow/outpaint/aspect-ratio - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-03 ## Pricing - **Base price**: $0.55/request - **Note**: Per image - outpainting to target aspect ratio # Outpaint (Aspect Ratio) API **Endpoint:** `POST /v1/models/runflow/outpaint/aspect-ratio/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/outpaint/aspect-ratio/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/aspect-ratio/examples/2/before.png", "aspect_ratio": "9:16" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/outpaint/aspect-ratio/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/aspect-ratio/examples/2/before.png", "aspect_ratio": "9:16" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/outpaint/aspect-ratio/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "image_url": "https://public.runflow.io/images/models/runflow/outpaint/aspect-ratio/examples/2/before.png", "aspect_ratio": "9:16" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the inputs to process - typically a `.zip` produced by the frontend. bg-brain downloads it to /tmp/inputs/ and unzips with filenames preserved. HTTPS required. | | `justification` | string | optional | `center`, `manual`, `top-left`, `bottom-right`, `expand-all` | Where to anchor the source image inside the expanded aspect ratio. "center" pads all sides equally. "top-left" pins the top-left corner and pads bottom-right. "bottom-right" pins the bottom-right corner and pads top-left. "expand-all" pads all sides. "manual" uses the explicit top/bottom/left/right percentages on the workflow. | | `aspect_ratio` | string | required | Any | Target output aspect ratio. Examples: "1:1" (square), "16:9" (widescreen), "9:16" (portrait), "4:3", "3:4", "21:9" (cinematic). | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/outpaint/aspect-ratio) - [API Reference](https://app.runflow.io/models/runflow/outpaint/aspect-ratio?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Product Isolation Pricing: **$0.27/request**. Endpoint: `POST /v1/models/runflow/product-isolation/runs`. Isolate the main product from a busy scene onto a clean background, using an optional text prompt to disambiguate the subject. Built for studio-grade product photography at scale. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/product-isolation/runs - **Model ID**: runflow/product-isolation - **Provider**: Runflow - **Last Updated**: 2026-05-05 ## Pricing - **Base price**: $0.27/request - **Note**: Per image - product isolation # Product Isolation API **Endpoint:** `POST /v1/models/runflow/product-isolation/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/product-isolation/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "the terracotta ribbed tank midi dress and black ankle boots", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp", "resolution": "1K", "aspect_ratio": "1:1" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/product-isolation/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "the terracotta ribbed tank midi dress and black ankle boots", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp", "resolution": "1K", "aspect_ratio": "1:1" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/product-isolation/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "the terracotta ribbed tank midi dress and black ankle boots", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp", "resolution": "1K", "aspect_ratio": "1:1" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the source photo. HTTPS required. | | `aspect_ratio` | string | required | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9` | Output aspect ratio. Examples: "1:1" (square), "16:9" (widescreen), "9:16" (portrait), "4:3", "3:4", "21:9". | | `resolution` | string | required | `1K`, `2K`, `4K` | Output resolution tier. "1K" ≈ 1024px, "2K" ≈ 2048px, "4K" ≈ 4096px on the long side. | | `prompt` | string | optional | Any | Optional text describing what to isolate. If omitted, the model decides automatically. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `image_urls` | image_list | Processed image URLs. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/product-isolation) - [API Reference](https://app.runflow.io/models/runflow/product-isolation?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Reference Inpaint Pricing: **$0.55/request**. Endpoint: `POST /v1/models/runflow/reference-inpaint/runs`. Inpaint a region of a target photo using a reference image as the visual guide. Provide the target, a black-and-white mask of the area to fill, and a reference image. The model paints the masked region with content matching the reference. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/reference-inpaint/runs - **Model ID**: runflow/reference-inpaint - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-05 ## Pricing - **Base price**: $0.55/request - **Note**: Per image: reference inpaint # Reference Inpaint API **Endpoint:** `POST /v1/models/runflow/reference-inpaint/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/reference-inpaint/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "zip_url": "https://public.runflow.io/images/models/runflow/reference-inpaint/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png", "file_reference": "reference.png" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/reference-inpaint/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "zip_url": "https://public.runflow.io/images/models/runflow/reference-inpaint/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png", "file_reference": "reference.png" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/reference-inpaint/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "zip_url": "https://public.runflow.io/images/models/runflow/reference-inpaint/examples/1/inputs.zip", "file_mask": "mask.png", "file_input": "photo.png", "file_reference": "reference.png" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `zip_url` | file | required | Any | URL of a zip archive containing the target image, mask, and reference image. Use file_input + file_mask + file_reference to name the files inside. | | `file_input` | string | required | Any | Filename inside `zip_url` of the photo to edit (e.g. `photo.png`). | | `file_mask` | string | required | Any | Filename inside `zip_url` of the black-and-white mask. White pixels mark the area to inpaint, black pixels mark what to keep. | | `file_reference` | string | required | Any | Filename inside `zip_url` of the reference image whose visual content should fill the masked area. | | `prompt` | string | optional | Any | Optional override for the scene description. Leave empty to use the default. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/reference-inpaint) - [API Reference](https://app.runflow.io/models/runflow/reference-inpaint?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Skin Fix Pricing: **$0.12/request**. Endpoint: `POST /v1/models/runflow/skin-fix/runs`. Cleanup pass for portrait photography. Smoothes skin texture, fixes blemishes, preserves natural detail. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/skin-fix/runs - **Model ID**: runflow/skin-fix - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-28 ## Pricing - **Base price**: $0.12/request - **Note**: Per image - skin retouch # Skin Fix API **Endpoint:** `POST /v1/models/runflow/skin-fix/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/skin-fix/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/runflow/skin-fix/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/skin-fix/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "image_url": "https://public.runflow.io/images/models/runflow/skin-fix/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/skin-fix/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "image_url": "https://public.runflow.io/images/models/runflow/skin-fix/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. HTTPS required. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/skin-fix) - [API Reference](https://app.runflow.io/models/runflow/skin-fix?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Smart Resize Pricing: **$0.55/request**. Endpoint: `POST /v1/models/runflow/smart-resize/runs`. Resize an image to a target aspect ratio + resolution (1K, 2K, 4K) without distortion or cropping. Uses generative reframing to recompose the subject into the new frame. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/smart-resize/runs - **Model ID**: runflow/smart-resize - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-03 ## Pricing - **Base price**: $0.55/request - **Note**: Per image - smart resize (aspect ratio + resolution) # Smart Resize API **Endpoint:** `POST /v1/models/runflow/smart-resize/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/smart-resize/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/runflow/smart-resize/examples/6/before.png", "resolution": "1K", "aspect_ratio": "4:3" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/smart-resize/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "image_url": "https://public.runflow.io/images/models/runflow/smart-resize/examples/6/before.png", "resolution": "1K", "aspect_ratio": "4:3" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/smart-resize/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "image_url": "https://public.runflow.io/images/models/runflow/smart-resize/examples/6/before.png", "resolution": "1K", "aspect_ratio": "4:3" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the inputs to process - typically a `.zip` produced by the frontend. bg-brain downloads it to /tmp/inputs/ and unzips with filenames preserved. HTTPS required. | | `aspect_ratio` | string | required | Any | Target output aspect ratio. Examples: "1:1" (square), "16:9" (widescreen), "9:16" (portrait), "4:3", "3:4", "21:9" (cinematic), "2:3", "3:2". | | `resolution` | string | required | `1K`, `2K`, `4K` | Target output resolution tier. "1K" ≈ 1024px, "2K" ≈ 2048px, "4K" ≈ 4096px on the long side. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/smart-resize) - [API Reference](https://app.runflow.io/models/runflow/smart-resize?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Smart Segmentation Pricing: **$0.12/request**. Endpoint: `POST /v1/models/runflow/smart-segmentation/runs`. Segment a requested subject from an image using the SAM3 VLM workflow. Returns one or more mask/segmentation outputs from a prompt-guided image analysis pass. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/smart-segmentation/runs - **Model ID**: runflow/smart-segmentation - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-05-14 ## Pricing - **Base price**: $0.12/request - **Note**: Per image - SAM3 segmentation # Smart Segmentation API **Endpoint:** `POST /v1/models/runflow/smart-segmentation/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/smart-segmentation/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "prompt": "foreground subject", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/smart-segmentation/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "foreground subject", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/smart-segmentation/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "prompt": "foreground subject", "image_url": "https://public.runflow.io/images/models/runflow/product-isolation/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the inputs to process - typically a `.zip` produced by the frontend. bg-brain downloads it to /tmp/inputs/ and unzips with filenames preserved. HTTPS required. | | `file_input` | string | optional | Any | Internal compatibility filename. For normal image_url inputs, bg-brain derives this automatically from the URL via url_to_input_filename; do not expose it in examples or client docs. | | `prompt` | string | required | Any | Subject or region to segment from the image. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Array of result items, each with at minimum a `url` field. | | `seed` | json | Random seed used (null if N/A). | | `timing` | json | Provider timing breakdown (null if not surfaced). | | `nsfw_detected` | json | Provider NSFW flag (null if not surfaced). | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/smart-segmentation) - [API Reference](https://app.runflow.io/models/runflow/smart-segmentation?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Tag Removal Pricing: **$0.14/request**. Endpoint: `POST /v1/models/runflow/tag-removal/runs`. Remove price tags, stickers, and labels from product images while preserving the underlying material and lighting. Ideal for re-listing inventory or cleaning up user-submitted photography. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/runflow/tag-removal/runs - **Model ID**: runflow/tag-removal - **Provider**: Runflow - **License**: commercial - **Last Updated**: 2026-04-21 ## Pricing - **Base price**: $0.14/request - **Note**: Per image - tag removal # Tag Removal API **Endpoint:** `POST /v1/models/runflow/tag-removal/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/runflow/tag-removal/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/tag-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/runflow/tag-removal/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/tag-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/runflow/tag-removal/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "config": {}, "image_url": "https://public.runflow.io/images/models/runflow/tag-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `image_url` | image | required | Any | URL of the image to process. | | `config` | json | optional | See nested fields below | Optional per-image config (aspect_ratio). | ### `config` - nested fields Optional per-image config (aspect_ratio). | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `aspect_ratio` | string | optional | Suggested: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `2:3` | | ```json {} ``` ## Output schema | Field | Type | Description | | --- | --- | --- | | `image_urls` | image_list | Processed image URLs. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/runflow/tag-removal) - [API Reference](https://app.runflow.io/models/runflow/tag-removal?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Topaz Upscale - Image Pricing: **$0.08/image**. Endpoint: `POST /v1/models/topaz/upscale/image/runs`. Use the powerful and accurate topaz image enhancer to enhance your images. ## Overview - **Endpoint**: https://api.runflow.io/v1/models/topaz/upscale/image/runs - **Model ID**: topaz/upscale/image - **Provider**: Topaz Labs - **License**: commercial - **Last Updated**: 2026-04-08 ## Pricing - **Base price**: $0.08/image - **Note**: Up to 24MP # Topaz Upscale - Image API **Endpoint:** `POST /v1/models/topaz/upscale/image/runs` ## Run the model ### Python ```python import requests response = requests.post( "https://api.runflow.io/v1/models/topaz/upscale/image/runs", headers={"Authorization": "Bearer RUNFLOW_API_KEY"}, json={ "input": { "model": "Standard V2", "image_url": "https://public.runflow.io/images/models/photalabs/phota/example-output-0.webp", "autoprompt": True, "output_format": "png", "upscale_factor": 4, "face_enhancement": True, "subject_detection": "Foreground", "face_enhancement_strength": 0.8, "face_enhancement_creativity": 0.1 }, "callback_url": "https://your-server.com/webhook" }, ) data = response.json() print(data) ``` ### Node.js ```javascript const response = await fetch( "https://api.runflow.io/v1/models/topaz/upscale/image/runs", { method: "POST", headers: { "Authorization": "Bearer RUNFLOW_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "model": "Standard V2", "image_url": "https://public.runflow.io/images/models/photalabs/phota/example-output-0.webp", "autoprompt": true, "output_format": "png", "upscale_factor": 4, "face_enhancement": true, "subject_detection": "Foreground", "face_enhancement_strength": 0.8, "face_enhancement_creativity": 0.1 }, "callback_url": "https://your-server.com/webhook" }), } ); const data = await response.json(); console.log(data); ``` ### cURL ```bash curl -X POST https://api.runflow.io/v1/models/topaz/upscale/image/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "input": { "model": "Standard V2", "image_url": "https://public.runflow.io/images/models/photalabs/phota/example-output-0.webp", "autoprompt": true, "output_format": "png", "upscale_factor": 4, "face_enhancement": true, "subject_detection": "Foreground", "face_enhancement_strength": 0.8, "face_enhancement_creativity": 0.1 }, "callback_url": "https://your-server.com/webhook" } JSON ``` ## Request parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `input` | object | required | Model input parameters. See "Input schema" below. | | `callback_url` | string \| null | optional | Webhook URL - POSTed when the run reaches a terminal state. | | `metadata` | object \| null | optional | Arbitrary key-value pairs attached to the run. | ## Input schema | Field | Type | Required | Allowed values | Description | | --- | --- | --- | --- | --- | | `prompt` | string | optional | Any | Text prompt to guide generative upscaling (max 1024 chars). Applies to Redefine model only. | | `face_enhancement_creativity` | float | optional | Any | Creativity level for face enhancement. 0.0 means no creativity, 1.0 means maximum creativity. Ignored if face enhancement is disabled. | | `face_enhancement` | boolean | optional | Any | Whether to apply face enhancement to the image. Applies to standard enhance and Recovery V2 models. | | `model` | string | optional | `Low Resolution V2`, `Standard V2`, `CGI`, `High Fidelity V2`, `Text Refine`, `Recovery`, `Redefine`, `Recovery V2`, `Standard MAX`, `Wonder` | Model to use for image enhancement. | | `crop_to_fill` | boolean | optional | Any | | | `upscale_factor` | float | optional | Any | Factor to upscale the image by (e.g. 2.0 doubles width and height) | | `face_enhancement_strength` | float | optional | Any | Strength of the face enhancement. 0.0 means no enhancement, 1.0 means maximum enhancement. Ignored if face enhancement is disabled. | | `sharpen` | float | optional | Any | Sharpening level (0.0-1.0). Applies to Standard V2, Low Resolution V2, CGI, High Fidelity V2, Text Refine, and Redefine models. | | `detail` | float | optional | Any | Detail recovery level (0.0-1.0). Applies to Recovery V2 model only. | | `denoise` | float | optional | Any | Denoising level (0.0-1.0). Applies to Standard V2, Low Resolution V2, CGI, High Fidelity V2, Text Refine, and Redefine models. | | `output_format` | string | optional | `jpeg`, `png` | Output format of the upscaled image. | | `subject_detection` | string | optional | `All`, `Foreground`, `Background` | Subject detection mode for the image enhancement. Applies to standard enhance and Recovery V2 models. | | `strength` | float | optional | Any | Enhancement strength (0.01-1.0). Applies to Text Refine model only. | | `texture` | integer | optional | Any | Texture detail level for generative upscaling (1-5). Applies to Redefine model only. | | `image_url` | image | required | Any | Url of the image to be upscaled | | `autoprompt` | boolean | optional | Any | Enable automatic prompt generation for generative upscaling. Applies to Redefine model only. | | `creativity` | integer | optional | Any | Creativity level for generative upscaling (1-6). Higher values produce more creative/hallucinated details. Applies to Redefine model only. | | `fix_compression` | float | optional | Any | Compression artifact removal level (0.0-1.0). Applies to Standard V2, Low Resolution V2, High Fidelity V2, and Text Refine models. | ## Output schema | Field | Type | Description | | --- | --- | --- | | `outputs` | json | Unified output array - one entry per generated artifact with url/type/width/height/duration/etc. | | `nsfw_detected` | json | true if the provider flagged output as NSFW, false if cleared, null if not checked. | | `seed` | json | Deterministic seed used for generation, or null if the provider doesn't return one. | | `timing` | json | Provider timing info (inference_ms etc.), or null. | ## Callback payload When you provide a `callback_url`, Runflow POSTs to it once the run reaches a terminal state. | Field | Type | Description | | --- | --- | --- | | `event` | string | Event type: "run.completed", "run.failed", or "run.cancelled". | | `run_id` | string | The unique identifier of the run. | | `status` | string | Terminal status: "succeeded", "failed", or "cancelled". | | `output` | object \| null | The run output. Null if the run failed or was cancelled. | | `duration_ms` | number \| null | Total run duration in milliseconds. | | `created_at` | string \| null | ISO 8601 timestamp when the run was created. | | `completed_at` | string \| null | ISO 8601 timestamp when the run reached terminal state. | | `metadata` | object \| null | The metadata object passed at run creation, if any. | - **Retries:** 3 attempts with exponential backoff (1s, 2s). Retries on 5xx / network errors only. - **Headers:** `Runflow-Request-Id` is always sent. `Runflow-Signature` is sent if a signing secret is configured. ## Additional Resources - [Playground](https://app.runflow.io/models/topaz/upscale/image) - [API Reference](https://app.runflow.io/models/topaz/upscale/image?tab=api) - [Documentation](https://docs.runflow.io) - [OpenAPI](https://docs.runflow.io/api/openapi.public.json) ## Related Browse the catalog. Callbacks, polling, statuses. Handle async results. How requests bill out. --- # Quickstart Start an AI model run with one HTTP call, then receive the result by callback or polling. This quickstart uses [Background Removal](/models/runflow/background-removal) (`$0.045/request`). ## 1. Get a key Sign in at [app.runflow.io/settings/api-keys](https://app.runflow.io/settings/api-keys), create a key, and export it: ```bash export RUNFLOW_API_KEY="your_api_key_here" ``` ## 2. Run the model ```bash cURL curl -X POST https://api.runflow.io/v1/models/runflow/background-removal/runs \ -H "Authorization: Bearer $RUNFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "image_url": "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp" }, "callback_url": "https://your-server.com/webhook" }' ``` ```python Python import os, requests response = requests.post( "https://api.runflow.io/v1/models/runflow/background-removal/runs", headers={"Authorization": f"Bearer {os.environ['RUNFLOW_API_KEY']}"}, json={ "input": { "image_url": "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp", }, "callback_url": "https://your-server.com/webhook", }, ) print(response.json()) ``` ```javascript Node.js const response = await fetch( "https://api.runflow.io/v1/models/runflow/background-removal/runs", { method: "POST", headers: { "Authorization": `Bearer ${process.env.RUNFLOW_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ input: { image_url: "https://public.runflow.io/images/models/runflow/background-removal/examples/1/before.webp", }, callback_url: "https://your-server.com/webhook", }), }, ); console.log(await response.json()); ``` ## 3. Read the response The API returns the run record immediately: ```json { "id": "01J0...", "status_code": "queued", "model": "runflow/background-removal" } ``` Use the `id` to either poll [`GET /v1/runs/{id}`](/api-reference/runs/get-run) or wait for the callback at `callback_url`. See [Callbacks](/concepts/callbacks) for the async pattern. Got an `id`? You're done. ## What's next Browse the full catalog. Bearer tokens, key rotation. Handle the async result. Status codes and the error envelope.