Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.runflow.io/llms.txt

Use this file to discover all available pages before exploring further.

@runflow-io/studio ships the production Runflow Studio UI as a mountable component. Drop it into a <div>, 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.
  • 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:
bun add @runflow-io/studio @runflow-io/proxy
Add @runflow-io/sdk if you also use 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.
// 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.

Step 2: mount the Studio

import { mount } from "@runflow-io/studio";

const studio = mount("#studio", {
  urls: { runflowProxy: "/api/runflow" },
  theme: "auto",
});

// later, to tear it down
studio.unmount();
<div id="studio"></div>
The Studio injects its own stylesheet into <head> on mount. Pass injectStyles: false if you ship your own CSS.

Mount options

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 keyWhat lives thereProvided bySecurity must-haves
runflowProxyPOST /v1/models/{model}/runs, GET /v1/runs/{id}, GET /v1/health@runflow-io/proxyAdd authenticate, see hooks.
imageProxyGET /?url=<external> returns the bytes same-originYouValidate the URL host against a CDN allowlist and reject RFC1918 / 169.254.0.0/16 / localhost to prevent SSRF (CWE-918).
uploadPOST multipart returns { url } (writes to your storage)YouEnforce content-type and size limits before writing; the Studio does not sanitize uploads.
chatSSE chat agent (wraps your AI provider plus the tool catalogue)YouGate 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 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:
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. 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:
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.

JavaScript SDK

Server-side, proxy, and the tool DSL.

Browse models

Workflows backed by real models.

Authentication

Bearer header.

Pick a model

Decision tree.