Notifications & events
Everything that happens on the platform — a deploy finishing, a build failing, an environment being created, a malware scan completing — lands in one event spine. This page covers the two pull-based ways to consume it: the events feed (the raw lifecycle log) and notifications (the same events curated into a per-user inbox with read state and email preferences). The push-based way is webhooks, which delivers the same event types to your endpoint.
Events
Section titled “Events”GET /v1/events
Section titled “GET /v1/events”events:read Your lifecycle events, newest first — deploys, builds, environment changes, jobs, snapshots, malware scans, and site lifecycle. Cursor-paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
type |
string | no | Filter to a single event type, e.g. deploy.completed. |
limit |
integer | no | Page size, 1–100; default 50. |
cursor |
string | no | Opaque pagination cursor. |
curl "https://api.managed.dev/v1/events?type=deploy.completed&limit=20" \ -H "Authorization: Bearer mfk_live_..."events, err := client.Events.List(ctx, &forge.EventListOptions{Type: "deploy.completed"})for _, ev := range events.Items { fmt.Println(ev.CreatedAt, ev.Type, ev.ID)}mf events list --type deploy.completed{ "data": [ { "id": "evt_01J9...", "type": "deploy.completed", "site_id": "site_01J7...", "env_id": "env_01J8...", "job_id": "job_01J9...", "data": { "deployment_id": "dep_01J8..." }, "created_at": "2026-06-24T14:36:40Z" } ], "pagination": { "next_cursor": "eyJ0...", "has_more": true }, "request_id": "req_01J9..."}An event links back to the scope it touched — site_id, env_id, and the job_id
of the operation that produced it — so you can pivot from the feed to the
job or resource in one hop.
One catalog, three transports
Section titled “One catalog, three transports”Event types are catalog-driven and shared across the feed, notifications, and
webhooks: deploy.completed, build.failed,
env.created, snapshot.completed, malware.detected, job.failed, and so on.
Read the live catalog at GET /v1/webhook-endpoints/event-types, or browse the
event types reference. Each type carries a category and a
severity (info, warning, error, critical), which drive the notification
behavior below.
Notifications
Section titled “Notifications”Notifications are the same event spine delivered to a per-user inbox: each carries a
rendered title and body, a category, a severity, and a read_at timestamp
(null while unread). The dashboard’s bell is a curated subset of the same feed. Reads
need notifications:read; marking read and changing
preferences need notifications:write.
GET /v1/notifications
Section titled “GET /v1/notifications”notifications:read Your notifications, newest first, cursor-paginated.
| Parameter | Type | Required | Description |
|---|---|---|---|
unread |
boolean | no | Only unread notifications. |
bell |
boolean | no | Only the curated bell subset the dashboard dropdown shows; omit for the full feed. |
category |
string | no | One catalog category, e.g. deploy, security. |
severity |
string | no | info, warning, error, or critical. |
limit / cursor |
— | no | Page size (default 30) and cursor. |
{ "data": [ { "id": "ntf_01J9...", "type": "build.failed", "category": "build", "severity": "error", "site_id": "site_01J7...", "title": "Build failed on acme-store.com", "body": "The build for refs/heads/main failed in the composer step.", "payload": { "build_id": "build_01J8..." }, "read_at": null, "created_at": "2026-06-24T15:02:11Z" } ], "pagination": { "next_cursor": "eyJ0...", "has_more": true }, "request_id": "req_01J9..."}GET /v1/notifications/unread-count
Section titled “GET /v1/notifications/unread-count”notifications:read The unread count —
{ "unread": 3 } — cheap enough to poll for a badge.
POST /v1/notifications/read
Section titled “POST /v1/notifications/read”notifications:write Mark notifications read, by id or all at once. Returns the number updated.
| Parameter | Type | Required | Description |
|---|---|---|---|
ids |
string[] | one of | The notifications to mark read. |
all |
boolean | one of | true marks everything read. |
curl -X POST https://api.managed.dev/v1/notifications/read \ -H "Authorization: Bearer mfk_live_..." \ -H "Content-Type: application/json" \ -d '{ "all": true }'count, err := client.Notifications.UnreadCount(ctx)if err == nil && count > 0 { all := true _, err = client.Notifications.MarkRead(ctx, &forge.MarkReadParams{All: &all})}Email preferences
Section titled “Email preferences”GET · PUT /v1/notification-preferences
Section titled “GET · PUT /v1/notification-preferences”Read (notifications:read) or upsert (notifications:write) your per-category email preferences. Every catalog category defaults to email on; only email is configurable — in-app delivery is curated by the event catalog itself, so there is no per-category in-app toggle.
curl -X PUT https://api.managed.dev/v1/notification-preferences \ -H "Authorization: Bearer mfk_live_..." \ -H "Content-Type: application/json" \ -d '{ "preferences": [ { "category": "job", "email": false } ] }'{ "data": [ { "category": "deploy", "email": true }, { "category": "build", "email": true }, { "category": "security", "email": true }, { "category": "job", "email": false } ], "request_id": "req_01J9..."}Which consumption path?
Section titled “Which consumption path?”| You want | Use |
|---|---|
| Push delivery to your own service, signed and retried | Webhooks |
| A pollable audit trail of what happened, scriptable filters | GET /v1/events |
| A human inbox with read state, severities, and email | /v1/notifications |
| To follow one specific operation to completion | The job it returned |