Skip to main content
Wait for Event feature that allows you to pause workflow execution until an external event occurs. This feature enables you to build asynchronous workflows that can wait for user interactions, external system responses, or any other events without consuming compute resources.

How Wait for Event Works

When you use context.waitForEvent() in your workflow, Upstash Workflow automatically pauses execution and waits for an external notification to resume. This happens without keeping your serverless function running, making it cost-effective and reliable for event-driven workflows. Each waiter has a timeout duration to wait for the event and then fires automatically.
Wait for Event timeouts have limits based on your pricing plan:
  • Free: Maximum timeout of 7 days
  • Pay-as-you-go: Maximum timeout of 1 year
  • Fixed pricing: Custom timeouts (no limit)

Race Condition Between Wait and Notify

A race condition can occur when notify is called before waitForEvent is executed. In this scenario, the notification will be sent but no workflow will be waiting to receive it, causing the event to be lost.

Solutions

There are three ways to handle race conditions:
  1. Use lookback with workflowRunId (Recommended for targeted notifications)
  2. Use Webhooks (Recommended for general use)
  3. Check and retry (Manual approach)

Option 1: Lookback with workflowRunId

When you know which specific workflow run should receive the notification, you can provide a workflowRunId to enable lookback. The notification will be stored and delivered even if sent before waitForEvent:
import { Client } from "@upstash/workflow";

const client = new Client({ token: "<WORKFLOW_TOKEN>" });

// Trigger a workflow
const { workflowRunId } = await client.trigger({
  url: "https://your-app.com/api/process-order",
  body: { orderId: "123" }
});

// Immediately notify with lookback - no race condition!
await client.notify({
  eventId: "payment-verified",
  eventData: { verified: true },
  workflowRunId: workflowRunId, // Enables lookback
});

Option 2: Use Webhooks

Webhooks have built-in lookback and are safer against timing issues for general event handling.

Option 3: Check and Retry

Alternatively, you can check the response of the notify operation and retry if needed:
import { Client } from "@upstash/workflow";

const client = new Client({ token: "<WORKFLOW_TOKEN>" });

const result = await client.notify({
    eventId,
    eventData
});

// Check if any workflows were notified
if (result.waiters && result.waiters.length > 0) {
    console.log(`Notified ${result.waiters.length} workflows`);
    return result;
}

// If no workflows were waiting, wait and retry once
console.log("No workflows waiting, retrying in 5 seconds...");
await new Promise(resolve => setTimeout(resolve, 5000));

return await client.notify({
    eventId,
    eventData
});

Selecting an Event ID

When a workflow run waits on an event ID, it’s appended to a list of waiters for the event ID. When a notify request is sent, all workflow runs waiting for that event are notified sequentially. To avoid heavy notify operations, it’s recommended to use unique event IDs instead of generic ones. For example, instead of waiting on user-sent-verification, wait the workflow on user-{userId}-sent-verification event.