Subscribe to live run updates and stream AI output to your frontend using Trigger.dev Realtime.
Trigger.dev Realtime lets you subscribe to background runs from your frontend or backend and receive live updates as they execute. It is built on top of Server-Sent Events (SSE) and works with any framework that supports streaming responses.
Trigger.dev Realtime is designed to work seamlessly with LLM output streaming. Your task pipes tokens directly to the Realtime API, and subscribers receive them as they arrive.
Use streams.pipe() to forward an async iterable or ReadableStream to the Realtime API:
import { task, streams } from "@trigger.dev/sdk";import OpenAI from "openai";const openai = new OpenAI();export const chatTask = task({ id: "chat", run: async (payload: { prompt: string }) => { const completion = await openai.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: payload.prompt }], stream: true, }); // Pipe the OpenAI stream to Realtime subscribers const { stream, waitUntilComplete } = streams.pipe(completion); let fullText = ""; for await (const chunk of stream) { fullText += chunk.choices[0]?.delta?.content ?? ""; } // Ensure all chunks have been sent before the task completes await waitUntilComplete(); return { text: fullText }; },});
streams.pipe() returns the original stream so you can consume it locally in the task while
simultaneously forwarding chunks to subscribers. Call waitUntilComplete() before returning
to guarantee all chunks have been flushed.
All Realtime subscriptions require a Public Access Token. These are short-lived JWT tokens that are safe to expose to the browser. There are two ways to obtain one:
From the run handle — When you trigger a task, the returned handle includes a publicAccessToken that is already scoped to that run.
Generated server-side — Use auth.createPublicToken() to create tokens scoped to specific runs, tags, or batches.
import { auth, tasks } from "@trigger.dev/sdk";// Trigger a task from your backend (e.g., a Next.js route handler)const handle = await tasks.trigger("chat", { prompt: "Hello" });// The handle already contains a public token scoped to this runconst { id: runId, publicAccessToken } = handle;// Or create a custom-scoped tokenconst token = await auth.createPublicToken({ scopes: { read: { runs: [handle.id] }, },});
Pass the token to your React hooks or backend subscription via the accessToken option.