Advanced6 min read

Stream run events in the browser

Use stream tokens to show live agent progress in your frontend without exposing your API key.

The Forge SSE endpoint requires an API key, but browser EventSource cannot send an Authorization header. The solution: your backend mints a short-lived stream token, passes thestream_url to the browser, and the browser opens the EventSource with the token in the query string. Your frg_app_ key never touches client-side code.

Step 1: Create a run from your backend

// Node / edge function
import { ForgeClient } from '@forge/sdk'
const forge = new ForgeClient({ apiKey: process.env.FORGE_APP_KEY! })

const run = await forge.createRun({ team_id, title, brief })
console.log(run.id) // e.g. "abc123"

Step 2: Mint a stream token

const { stream_token, stream_url, expires_at } = await forge.createStreamToken(run.id)
// stream_url is ready-to-use: https://…/api/v1/runs/abc123/stream?stream_token=frg_strm_…
// expires_at is ~15 minutes from now

return Response.json({ stream_url })  // send to browser
Tip:Tokens last 15 minutes. If your UI may stay open longer (e.g. waiting for a long run), mint a new token when the user initiates streaming rather than at run creation time.

Step 3: Open EventSource in the browser

const { stream_url } = await (await fetch('/api/stream-url?run_id=' + runId)).json()

const src = new EventSource(stream_url)

src.addEventListener('event', (e) => {
  const evt = JSON.parse(e.data)
  console.log(evt.agent_role, evt.title)
})

src.addEventListener('status', (e) => {
  const { status, completed_agents, total_agents } = JSON.parse(e.data)
  setProgress(completed_agents / total_agents)
})

src.addEventListener('done', (e) => {
  const { status } = JSON.parse(e.data)
  console.log('Run finished:', status)
  src.close()
})

Step 4: Handle reconnects

The browser reconnects automatically if the connection drops. Each event: frame carries an id:line (the sequence number) so the browser sends a Last-Event-ID header on reconnect. Forge resumes the stream from that sequence — no events are replayed. Your client code needs no special reconnect logic.

Tip:If you need to resume from a specific point manually, add ?since=<sequence_number> to the stream URL.
Stream run events in the browser — Forge Guides — Forge