docs/content/Agents/notifications.mdx
import { Callout } from 'nextra/components'
DocsGPT pushes realtime updates to the browser over Server-Sent Events (SSE). This is what powers the upload toasts, tool-approval prompts, and other live notifications in the UI. There are two channels:
GET /api/events: a per-user notification stream (ingestion progress, tool approvals, MCP OAuth completion, …).GET /api/messages/<message_id>/events: resume an answer stream that was interrupted mid-generation.Open an SSE connection to receive notifications for the authenticated user:
GET /api/events
Accept: text/event-stream
Authorization: Bearer <token>
Each event is a JSON object with a type field, for example:
id: 1718900000000-0
data: {"type":"source.ingest.queued","scope":{"id":"<source_id>"}, ...}
Common event types:
| Type | Meaning |
|---|---|
source.ingest.queued | A source ingestion task was enqueued (drives the upload toast). |
mcp.oauth.completed | An MCP server's OAuth handshake finished. |
| tool-approval events | An agent is requesting approval to run a tool. |
backlog.truncated | The client's cursor slid off the retained backlog window — clear your cursor and refetch state. |
Events are journaled per user in a Redis Stream so a client that reconnects can catch up on what it missed. Send the last id you processed and DocsGPT replays everything after it:
GET /api/events
Last-Event-ID: 1718900000000-0
(You may also pass it as a last_event_id query parameter.) Each delivered event carries its own id:, so your cursor advances as you read. If you fall a long way behind, the snapshot is delivered across several reconnects rather than all at once.
A few bounded behaviors to be aware of:
EVENTS_STREAM_MAXLEN entries (default 1000). If your Last-Event-ID is older than the oldest retained entry, you receive a backlog.truncated event — reset your cursor and refetch current state.EVENTS_REPLAY_MAX_PER_REQUEST entries per request (default 200); reconnect to continue.SSE_MAX_CONCURRENT_PER_USER, default 8) and a windowed replay budget. Exceeding either returns HTTP 429 — back off and retry.When an answer is streaming and the connection drops, resume it without losing the in-progress generation:
GET /api/messages/<message_id>/events
This replays the message's events past your last-seen sequence number and tails the rest live. It is backed by the Postgres message_events journal (retained for MESSAGE_EVENTS_RETENTION_DAYS, default 14).
| Setting | Default | Purpose |
|---|---|---|
ENABLE_SSE_PUSH | true | Master switch for the publisher and channel. |
EVENTS_STREAM_MAXLEN | 1000 | Per-user backlog cap (approximate). |
SSE_KEEPALIVE_SECONDS | 15 | Keepalive comment-frame cadence (keep below your proxy's idle timeout). |
SSE_MAX_CONCURRENT_PER_USER | 8 | Max simultaneous SSE connections per user (0 disables the cap). |
EVENTS_REPLAY_MAX_PER_REQUEST | 200 | Max backlog entries per replay request. |
EVENTS_REPLAY_BUDGET_REQUESTS_PER_WINDOW | 30 | Per-user replay requests per window (0 disables). |
EVENTS_REPLAY_BUDGET_WINDOW_SECONDS | 60 | Replay budget window length. |
MESSAGE_EVENTS_RETENTION_DAYS | 14 | Retention for the chat-stream message_events journal. |