---
name: fal-ai-media-api-orchestration
description: "Drive fal.ai's queue API, WebSocket streaming, webhooks, and multi-model chains across FLUX, Kling, Veo and more. Production fal.ai pipelines: fal.subscribe vs fal.queue.submit/status/result polling, request_id lifecycle and webhook (fal_webhook) callbacks with signature verification, real-time WebSocket streaming via fal.stream, file uploads through fal.storage, and chaining models (FLUX text-to-image into Kling/Veo image-to-video, Wan, Seedream, audio) into multi-step workflows with logs, queue position, and retry/timeout handling. Covers fal-client Python and @fal-ai/client JS, FAL_KEY auth, and serverless GPU model endpoints."
license: MIT
---

# fal.ai Media Orchestrator

A focused skill for building production pipelines on top of [fal.ai](https://fal.ai) — the inference platform for generative media models like FLUX, Kling, Veo, Stable Diffusion, and Whisper. It teaches the agent how to choose the right execution mode (synchronous subscribe vs. queued polling vs. webhook callbacks), wire up real-time streaming, handle file uploads, and chain one model's output into the next. Aimed at backend and ML engineers integrating fal into APIs, workers, or media generation services.

## When to use it

- You're calling `@fal-ai/client` (JS) or `fal-client` (Python) and need to decide between blocking and queued execution.
- A request runs longer than your HTTP timeout (most video models) and you need queue submission with `request_id` polling or webhooks.
- You're building a multi-step pipeline — e.g. generate a still with FLUX, then animate it with Kling or Stable Video Diffusion.
- You need webhook callbacks delivered to your server and want to verify they actually came from fal.

## How it works

The skill covers the real client surface:

- **`fal.subscribe(modelId, { input, logs })`** — blocking call that submits, polls, and returns the result in one await. Best for fast image models.
- **`fal.queue.submit` / `fal.queue.status` / `fal.queue.result`** — the manual queue lifecycle. `submit` returns a `request_id`; poll `status` (`IN_QUEUE`, `IN_PROGRESS`, `COMPLETED`) then fetch `result`.
- **`fal_webhook`** — pass a webhook URL on submit so fal POSTs the result to you instead of polling. Verify the `x-fal-webhook-signature` / Ed25519 headers against fal's published JWKS before trusting the payload.
- **`fal.stream(modelId, { input })`** — async iterable of partial events over WebSocket for models that emit incremental output.
- **`fal.storage.upload(file)`** — upload local files and pass the returned URL as model input (e.g. an init image for image-to-video).

## Usage

```ts
import { fal } from "@fal-ai/client";
fal.config({ credentials: process.env.FAL_KEY });

// 1. FLUX text-to-image (fast → subscribe)
const img = await fal.subscribe("fal-ai/flux/dev", {
  input: { prompt: "a neon koi pond at dusk", image_size: "landscape_16_9" },
  logs: true,
});

// 2. Animate it (slow → queue + webhook)
const { request_id } = await fal.queue.submit("fal-ai/kling-video/v1/standard/image-to-video", {
  input: { image_url: img.data.images[0].url, prompt: "gentle ripples" },
  webhookUrl: "https://api.example.com/hooks/fal",
});
```

## Tips & gotchas

- Never ship `FAL_KEY` to the browser — proxy through a server route (`@fal-ai/server-proxy`) so the key stays server-side.
- For video, prefer webhooks or queue polling over `subscribe`; long jobs will blow past serverless function timeouts.
- Webhook deliveries can retry — make your handler idempotent on `request_id`.
- `image_url` inputs accept either a fal.storage URL or any public URL; large local assets must go through `fal.storage.upload` first.

## FAQ

**Subscribe vs. queue — which?** Use `subscribe` for sub-30s image jobs; use the queue (with webhooks) for video and batch work that outlives your request timeout.

**How do I trust a webhook?** Verify the request signature against fal's public keys (JWKS) before reading the body — don't act on unverified POSTs.

**Can I cancel a queued job?** Yes — the queue API exposes a cancel operation keyed by `request_id`.
