Some workflows require human approval or input before proceeding. When combined with Upstash Realtime, you can create interactive workflows that pause for user input and provide real-time feedback to your frontend during the entire process.
This guide shows you how to implement a human-in-the-loop workflow pattern with real-time updates using Upstash Workflow and Upstash Realtime.
How It Works#
In a human-in-the-loop workflow:
- The workflow executes initial steps and emits progress events
- The workflow pauses at a specific point using
context.waitForEvent() - A "waiting for input" event is emitted to notify the frontend
- The user makes a decision in the frontend (approve/reject)
- The frontend calls an API to notify the workflow using
client.notify() - The workflow resumes with the user's decision
- An "input resolved" event is emitted so the frontend can update its UI
- The workflow continues and completes based on the decision
Prerequisites#
- An Upstash account with:
- A QStash project for workflows
- A Redis database for Realtime
- Next.js application set up
- Completed the basic real-time workflow setup
Event Types#
For human-in-the-loop workflows, extend your schema in lib/realtime.ts with these additional event types:
The new event types are:
waitingForInput: Emitted when the workflow pauses and needs user inputinputResolved: Emitted when the user provides input, so the frontend knows to clear the waiting state
Create the Realtime Middleware#
Create a custom middleware that will emit events to Realtime at lib/middleware.ts:
Key points:
- The middleware handles all realtime event emissions automatically
beforeExecution: Detects wait-for-event steps by checking the stepName and emitsworkflow.waitingForInputafterExecution: Emitsworkflow.inputResolvedfor wait steps andworkflow.stepFinishfor all stepsrunCompleted: Emitsworkflow.runFinishwhen the workflow finishes- All emission logic is centralized in the middleware, keeping workflow code clean
Building the Workflow#
1. Create the Workflow Endpoint#
Create your workflow at app/api/workflow/human-in-loop/route.ts:
Key patterns:
- Middleware for all events: The
realtimeMiddlewareautomatically handles all realtime event emissions by detecting wait-for-event steps through the stepName - Step name detection: The middleware checks if
stepName === "wait-for-approval"to know when to emitwaitingForInputandinputResolvedevents - Unique event IDs: Use a unique
eventId(likeapproval-${workflowRunId}) to identify which approval request this is - Timeout handling: Always handle the timeout case when waiting for events
2. Create the Notify Endpoint#
Create an endpoint at app/api/notify/route.ts to handle user input:
Preventing Race Conditions: If you need to trigger a workflow and immediately send it an event, you can use the workflowRunId parameter to enable lookback:
Learn more in the notify documentation.
Building the Frontend#
1. Extend the Custom Hook#
Extend your hook from the basic example to handle waiting states:
Key additions:
waitingState: Tracks when the workflow is waiting for inputcontinueWorkflow: Function to submit user decisions back to the workflow- Multiple events subscription: Uses
eventsarray to subscribe to multiple event types - Input resolved handling: Clears the waiting state when the workflow receives the user's input
2. Use the Hook with Approval UI#
How the Pattern Works#
Timeline of Events#
- Initial processing:
stepFinishevent → Frontend shows completed step - Waiting for approval:
waitingForInputevent → Frontend shows approval buttons - User clicks approve/reject: Frontend calls
/api/notify - Workflow resumes:
inputResolvedevent → Frontend hides approval buttons - Processing continues: More
stepFinishevents as workflow continues - Workflow completes:
runFinishevent → Frontend shows "Workflow Finished!"
Benefits#
- Real-time feedback: Users see exactly when their approval is needed
- No polling: Instant updates via Server-Sent Events
- Timeout handling: Workflows don't hang indefinitely waiting for input
Full Example#
For a complete working example with all steps, error handling, and full UI components, check out the Upstash Realtime example on GitHub.
Next Steps#
- Review the basic real-time workflow pattern
- Learn about workflow event handling
- Explore Realtime features
- Check out workflow failure handling