Live updates (Server-Sent Events)
The management API exposes a Server-Sent Events (SSE) stream so open browser sessions can react when configuration changes are saved—without polling and without putting full resource bodies on the event bus.
Endpoint
| Method | Path | Auth |
|---|---|---|
GET | /api/v1/events/stream | Management API OIDC session (same roles as other management routes; viewer and above can connect) |
Send the same Bearer access token you use for other management API calls. The response has Content-Type: text/event-stream. The server may send comment lines (: ping) as keep-alives.
Event shape
Each data: line is a JSON object. There is no full resource body in the payload—only identifiers and change hints so clients can decide whether to refetch.
Common fields
| Field | Meaning |
|---|---|
schema_version | Envelope version (currently 1). |
resource_kind | proxy_workflow, collection, proxy, credential, policy, client, profile, or future kinds. |
actor_sub | OIDC sub of the user who performed the persist (when available). |
occurred_at_unix | Unix seconds when the event was emitted. |
When resource_kind is proxy_workflow
| Field | Meaning |
|---|---|
proxy_id | Affected proxy. |
definition_changed | true if the workflow definition content changed. |
enabled_changed | true if the workflow enabled flag changed. |
workflow_enabled | Current enabled state after the persist. |
When resource_kind is collection
| Field | Meaning |
|---|---|
collection_id | Primary collection id for the operation (source id for duplicated). |
operation | One of: created, updated, deleted, status_changed, duplicated, imported, openapi_imported. |
related_collection_id | Set for duplicated — the new collection id. |
When resource_kind is proxy
Proxy metadata CRUD (not workflow definition saves—those use proxy_workflow). No full proxy body on the wire.
| Field | Meaning |
|---|---|
proxy_id | Affected proxy. |
collection_id | Collection that owns the proxy (for client-side filtering). |
operation | One of: created, updated, deleted. |
When resource_kind is credential
API credentials CRUD (stored secrets metadata—not connection validation). No secrets on the wire.
| Field | Meaning |
|---|---|
credential_id | Affected credential record. |
operation | One of: created, updated, deleted. |
When resource_kind is policy
API access policies (rate limits / quotas). No policy body on the wire.
| Field | Meaning |
|---|---|
policy_id | Affected policy. |
operation | One of: created, updated, deleted, status_changed. |
When resource_kind is client
Management API client records (distinct from OAuth client terminology). No client body on the wire.
| Field | Meaning |
|---|---|
client_id | Affected API client id. |
operation | One of: created, updated, deleted, status_changed. |
When resource_kind is profile
Management access profiles (nested under an API client). No profile body on the wire. The client_id field is the parent API client id (not a resource_kind: client event).
| Field | Meaning |
|---|---|
profile_id | Affected access profile id. |
client_id | Parent API client id (owner). |
operation | One of: created, updated, deleted. |
What emits events today
Workflow (resource_kind: proxy_workflow)
| Trigger | Emits |
|---|---|
Successful PUT /api/v1/proxies/:id/workflow (save definition and/or toggle enabled) | Yes |
Validation-only and workflow test/stream endpoints do not emit workflow events.
Collection (resource_kind: collection)
| Trigger | operation |
|---|---|
POST /api/v1/collections | created |
PUT /api/v1/collections/:id | updated |
DELETE /api/v1/collections/:id | deleted |
PUT /api/v1/collections/:id/status | status_changed |
POST /api/v1/collections/:id/duplicate | duplicated (related_collection_id = new id) |
POST /api/v1/collections/.../import (multipart import) | imported |
POST /api/v1/collections/import/openapi | openapi_imported |
Read-only routes (list, get, export file, OpenAPI download, bulk export) do not emit.
Proxy metadata (resource_kind: proxy)
| Trigger | operation |
|---|---|
POST /api/v1/proxies | created |
PUT /api/v1/proxies/:id | updated |
DELETE /api/v1/proxies/:id | deleted |
Workflow definition updates use proxy_workflow (see above), not proxy.
API credentials (resource_kind: credential)
| Trigger | operation |
|---|---|
POST /api/v1/credentials | created |
PUT /api/v1/credentials/:id | updated |
DELETE /api/v1/credentials/:id | deleted |
GET list/detail, POST /api/v1/credentials/validate, and other non-persisting routes do not emit.
API access policies (resource_kind: policy)
| Trigger | operation |
|---|---|
POST /api/v1/policies | created |
PUT /api/v1/policies/:id | updated |
PUT /api/v1/policies/:id/toggle-status | status_changed |
DELETE /api/v1/policies/:id | deleted |
GET list/detail do not emit.
API clients (resource_kind: client)
| Trigger | operation |
|---|---|
POST /api/v1/clients | created |
PUT /api/v1/clients/:id | updated |
PUT /api/v1/clients/:id/status | status_changed |
DELETE /api/v1/clients/:id | deleted |
GET list/detail do not emit.
Access profiles (resource_kind: profile)
| Trigger | operation |
|---|---|
POST /api/v1/clients/:clientId/profiles | created |
PUT /api/v1/clients/:clientId/profiles/:id | updated |
POST /api/v1/clients/:clientId/profiles/:id/rotate | updated (after a successful rotate; OIDC/mTLS no-op responses do not emit) |
PUT /api/v1/clients/:clientId/profiles/:id/auth-type | updated |
DELETE /api/v1/clients/:clientId/profiles/:id | deleted |
GET list/detail/credentials and GET /api/v1/auth/config do not emit.
UI behavior (Workflow Builder)
On the proxy Workflow Builder page, if your local editor has no unsaved changes and an event arrives for the same proxy_id and proxy_workflow, the UI refetches the workflow from the API. If you have unsaved edits, the UI shows a non-destructive banner so you can reload when ready or keep editing.
Sessions opened via Zerq Copilot or Management MCP use the same PUT workflow API; saves from those tools produce the same events for other tabs.
UI behavior (Collection detail and edit)
On Collection detail (/collections/:id), matching collection events refetch collection and proxy list data. deleted navigates back to the collection list. duplicated shows a short notice with the new collection id.
A separate subscription uses resource_kind: proxy for the same collection_id: when another session creates, updates, or deletes a proxy in that collection, the page refetches the proxy list only (no full navigation). This keeps the proxy table in sync without conflating workflow-definition saves (proxy_workflow) with proxy CRUD.
On Collection edit, a banner offers Reload to pull the latest server state without silently overwriting an in-progress form.
On the Collections list (/collections), the UI subscribes with useManagementEvents({ collectionList: true }) and silently refetches the list when any resource_kind: collection event arrives (create, update, delete, etc.), so other tabs or users’ changes appear without a manual refresh.
On the API Credentials list (/credentials), the UI subscribes with useManagementEvents({ credentialsList: true }) and silently refetches when any resource_kind: credential event arrives. On Edit credentials (/credentials/edit/:id), a banner with Reload appears when an updated event targets the same credential; deleted navigates back to the list.
On the API Access Policies page (/policies), the UI subscribes with useManagementEvents({ policiesList: true }) and silently refetches the grid when any resource_kind: policy event arrives. The create and edit modals subscribe by policy_id when editing an existing policy: remote updated / status_changed shows a Reload banner; remote deleted closes the modal and refreshes the list.
On the API Clients page (/clients), the UI subscribes with useManagementEvents({ clientsList: true }) and silently refetches clients plus collection/policy dropdown data when any resource_kind: client event arrives. The create and edit modals subscribe by client_id when editing an existing client: remote updated / status_changed shows a Reload banner; remote deleted closes the modal and refreshes the list.
On the Access profiles page for an API client (/clients/:id/profiles), the UI subscribes with useManagementEvents({ accessProfilesForClient: true, parentClientId }) and silently refetches when any resource_kind: profile event targets that parent client. The edit profile modal and authentication modal subscribe by profile_id when open: remote updated shows a Reload banner; remote deleted closes the modal and refreshes the list.
Operational notes
- With Redis cache enabled, events are also published on a Redis channel so multiple API pods each fan out to their local SSE subscribers.
- Without Redis, events are delivered in-process only (suitable for single-node dev).
For package layout, Redis channel name, and subscriber roadmap, operators and engineers should read the internal runbook at zerq-docs/docs/_internal/management-domain-events-sse.md in the repository.