Skip to main content

Overview

This full-stack example shows how to build a visual, node-based workflow where each step in the pipeline is a Trigger.dev task and a human reviewer must approve an AI-generated summary before the audio version is created. Tech stack:

GitHub repo

View the human-in-the-loop workflow repo

Fork this repo to use it as a starting point for your own human-in-the-loop project.

Demo video

How it works

The workflow consists of three Trigger.dev tasks wired together by an orchestrator, represented visually as ReactFlow nodes:
  1. summarizeArticle — calls the OpenAI API to produce a plain-text summary from a URL
  2. reviewSummary — pauses the workflow and waits for a human to approve or reject the summary
  3. convertTextToSpeech — sends the approved summary to ElevenLabs and uploads the resulting audio to S3
  4. articleWorkflow — the entrypoint that orchestrates all three tasks in sequence

Waitpoint token pattern

The human review step is powered by Trigger.dev waitpoint tokens. The token is created inside a Next.js server action when the workflow starts:
app/actions.ts
import { wait } from "@trigger.dev/sdk";

const reviewWaitpointToken = await wait.createToken({
  tags: [workflowTag],
  timeout: "1h",
  idempotencyKey: `review-summary-${workflowTag}`,
});
The reviewSummary task then suspends by waiting for that token:
trigger/reviewSummary.ts
import { task, wait } from "@trigger.dev/sdk";

export const reviewSummary = task({
  id: "review-summary",
  run: async (payload: { summary: string; tokenId: string }) => {
    // The task pauses here — no compute resources are consumed while waiting
    const result = await wait.forToken<{ approved: boolean; approvedBy: string }>(
      { id: payload.tokenId }
    );

    if (!result.ok) {
      throw new Error("Review timed out or was cancelled");
    }

    return result.output;
  },
});
When the reviewer clicks Approve in the ReactFlow UI, another server action completes the token:
app/actions.ts
import { wait } from "@trigger.dev/sdk";

await wait.completeToken<{ approved: boolean; approvedAt: Date; approvedBy: string }>(
  { id: tokenId },
  {
    approved: true,
    approvedAt: new Date(),
    approvedBy: user,
  }
);
Once the token is completed, the reviewSummary task resumes and the orchestrator moves on to the convertTextToSpeech step.

Real-time status with Realtime hooks

The ReactFlow UI shows the live status of each node by subscribing to task runs using the useRealtimeRunsWithTag hook:
components/Flow.tsx
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";

// Subscribe to all runs tagged with the current workflow ID
const { runs } = useRealtimeRunsWithTag(workflowTag);
Each ActionNode receives the relevant run object and displays its current status (queued, executing, completed, failed) in real time.

ReactFlow node types

NodePurpose
InputNodeStarting node — accepts an article URL and triggers the workflow
ActionNodeDisplays the real-time status of a Trigger.dev task run
ReviewNodeShows the AI-generated summary and prompts for human approval

Learn more

Waitpoint tokens

Learn how waitpoint tokens work and how to build human-in-the-loop flows

Trigger.dev Realtime

Subscribe to runs and stream live updates to your frontend

React hooks

Use React hooks to interact with the Trigger.dev API from your UI

Wait for token

Pause tasks and wait for human approval using waitpoint tokens

Build docs developers (and LLMs) love