We currently provide a wait–notify mechanism that allows a workflow run to pause on an event ID and resume when a notify call is received. This works well when developers control both sides of the communication and can send the notification directly.
However, when a workflow needs to wait for a notification from a third-party provider, developers must register a waiter for a specific event ID, expose a custom endpoint to receive the third-party webhook, and then manually call our notify API with the event ID to resume the workflow. This process adds overhead and can lead to race conditions.
To improve the developer experience for webhook-based integrations and eliminate race conditions, we're introducing a new native webhook wait API. This API allows a workflow run to pause execution until an auto-generated webhook URL receives a notification. The generated URL can be passed directly to third-party providers, which will automatically notify the corresponding workflow run.
Each webhook can receive multiple notifications and be awaited multiple times.
Below is an early draft of the API (subject to change before release):
{
// 👇 Create a webhook to recieve events
const webhook = await context.createWebhook("fal-generation-webhook");
await context.run("start-generation", async () => {
const { request_id } = await fal.queue.submit("fal-ai/flux/dev", {
input: {
prompt: "a cat",
seed: 6252023,
image_size: "landscape_4_3",
num_images: 4,
},
// 👇 Pass webhook.url to third-party service
webhookUrl: webhook.url,
});
return request_id
});
// 👇 Wait for webhook to be called
const result = await context.waitForWebhook("wait-until-generation-completes", webhook, "1d");
await context.run("send-generation-to-user", async () => {
// 👇 result.timeout OR result.request
const req = result.request!;
// 👇 Use the native Request object of the webhook call
const data = await req.json();
console.log("Generation completed:", data.status);
});
} ); ">import { serve } from "@upstash/workflow/nextjs"; import { fal } from "@fal-ai/client";
export const { POST } = serve( async (context) => {
// 👇 Create a webhook to recieve events
const webhook = await context.createWebhook("fal-generation-webhook");
await context.run("start-generation", async () => {
const { request_id } = await fal.queue.submit("fal-ai/flux/dev", {
input: {
prompt: "a cat",
seed: 6252023,
image_size: "landscape_4_3",
num_images: 4,
},
// 👇 Pass webhook.url to third-party service
webhookUrl: webhook.url,
});
return request_id
});
// 👇 Wait for webhook to be called
const result = await context.waitForWebhook("wait-until-generation-completes", webhook, "1d");
await context.run("send-generation-to-user", async () => {
// 👇 result.timeout OR result.request
const req = result.request!;
// 👇 Use the native Request object of the webhook call
const data = await req.json();
console.log("Generation completed:", data.status);
});
} );
Fetched April 19, 2026