Reference

Webhooks

For anything that runs longer than a browser is willing to wait, register a webhook on submit and take the result whenever it lands. Cheaper than polling, kinder to users.

Registering a webhook

Append ?fal_webhook=<url> to any queue submit. fal posts the final result to the URL you provide. The URL must be reachable over HTTPS from the public internet.

01example.shBASH
01curl -X POST "https://queue.fal.run/fal-ai/ernie-image?fal_webhook=https://your.app/api/fal/webhook" \
02 -H "Authorization: Key $FAL_KEY" \
03 -H "Content-Type: application/json" \
04 -d '{"prompt":"A vertical coffee shop poster. Large centered headline reads 'MORNING POUR' i...","aspect_ratio":"1:1","num_images":1,"enable_prompt_enhancer":true,"num_inference_steps":50,"seed":0}'

Payload shape

The body is JSON with a stable top-level shape. The nested payload matches the model's own output schema, identical to what /docs/api-reference documents for synchronous calls.

01example.jsonJSON
01{
02 "request_id": "019d9e0b-6eb0-7920-827a-868d042ec76e",
03 "gateway_request_id": "01jav3...zy5",
04 "status": "OK",
05 "payload": {
06 "images": [ { "url": "https://v3b.fal.media/files/..." } ],
07 "seed": 4221
08 }
09}

Handler (Node / Express)

fal may deliver the same webhook more than once on retry. Always dedupe on the request_id. Respond with a 2xx as soon as your write is durable; long handlers risk a redelivery.

01example.tsTS
01import express from "express";
02
03const app = express();
04app.use(express.json());
05
06app.post("/api/fal/webhook", async (req, res) => {
07 // Dedupe by request_id: fal may deliver the same webhook twice on retries.
08 const { request_id, status, payload, error } = req.body;
09 if (!request_id) return res.status(400).end();
10
11 // Idempotent write: upsert on request_id.
12 await db.generations.upsert({
13 where: { request_id },
14 update: { status, result: payload, error },
15 create: { request_id, status, result: payload, error },
16 });
17
18 res.status(200).end();
19});

Rules

  • Idempotent writes only. Upsert on request_id.
  • Accept body within 10 seconds; return 2xx immediately; process asynchronously if needed.
  • Treat the endpoint as public. Sign and verify if you handle sensitive payloads; rotate the signing secret periodically.
  • Fall back to polling (/docs/async-tasks) when you cannot expose a public URL.
Also reading