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 browserStep 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.
?since=<sequence_number> to the stream URL.