Notifications API
Manage delivery channels, per-event subscriptions, org defaults, and the in-app alert feed.
The Notifications API decides who hears about events like a failed deploy or an expiring SSL cert, and how. You register channels (where alerts go — email, webhook, Slack, or the in-app bell), toggle subscriptions (which event categories reach each channel), set org-wide defaults for new members, and read deliveries (the alert feed behind the bell icon). In the dashboard this is the Settings → Notifications page. Channels, subscriptions, and deliveries are per-user; defaults are per-org.
Base path & auth
All paths are relative to your instance, under /api — e.g. https://your-host/api/notifications/channels.
Send a personal access token as a bearer header (Authorization: Bearer <token>), created with
openship token create. The dashboard uses your session cookie instead. See the
API overview and the auth model for details.
Available on every instance
This module ships on both self-hosted and cloud instances. There is no dedicated openship CLI command —
manage notifications from the dashboard or these endpoints directly.
Endpoints
| Method & path | Permission | What it does |
|---|---|---|
GET /api/notifications/categories | notifications:read | List notification categories (the registry of event types). |
GET /api/notifications/channels | notifications:read | List the caller's notification channels (email, webhook, etc.). |
POST /api/notifications/channels | notifications:write | Create a notification channel. |
PATCH /api/notifications/channels/:id | notifications:write | Update a notification channel. |
DELETE /api/notifications/channels/:id | notifications:write | Delete a notification channel. |
GET /api/notifications/subscriptions | notifications:read | List the caller's notification subscriptions. |
PUT /api/notifications/subscriptions | notifications:write | Create or update a notification subscription. |
DELETE /api/notifications/subscriptions/:id | notifications:write | Delete a notification subscription. |
GET /api/notifications/defaults | notifications:read | List org default notification settings. |
PUT /api/notifications/defaults | notifications:admin | Upsert one org default. |
GET /api/notifications/deliveries | notifications:read | List notification deliveries (the in-app alert feed). |
GET /api/notifications/deliveries/unseen-count | notifications:read | Count unseen notifications (for the bell badge). |
POST /api/notifications/deliveries/:id/seen | notifications:write | Mark one delivery as seen. |
Ownership is enforced inside the handlers
The notifications:* tags only check that you're a member of the active org. Beyond that, channels,
subscriptions, and deliveries are scoped to your user — you can't read or modify another member's. A
per-user route for an id you don't own returns 404.
Categories
Categories are the stable, user-facing event groups (e.g. deploy.failed, backup.failed,
domain.expiring) that subscriptions and defaults key off. They live in code, not the database, so GET /api/notifications/categories is a static registry — each entry has an id, label, description, and
defaultEnabled.
curl https://your-host/api/notifications/categories \
-H "Authorization: Bearer $OPENSHIP_TOKEN"Create a channel
A channel is a destination. The config object is shape-checked per kind; secrets in it (webhook signing
keys, Slack URLs) are stored encrypted and never returned in full.
POST /api/notifications/channelsProp
Type
The config shape depends on kind:
Prop
Type
curl -X POST https://your-host/api/notifications/channels \
-H "Authorization: Bearer $OPENSHIP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"email","label":"On-call inbox","config":{"address":"ops@example.com"}}'New channels start enabled: true. An in_app channel is verified immediately; every other kind starts
unverified and must be verified (via a test send) before it will deliver — flip verified with PATCH.
Update a channel
Send only the fields you want to change. Changing config on a non-in_app channel resets verified to
false, since the destination may have moved.
PATCH /api/notifications/channels/:idProp
Type
Subscribe a channel to a category
A subscription is one toggle in the (user, org, category, channel) grid. PUT is an idempotent upsert — send
the same triple again to flip enabled. The channelId must be a channel you own.
PUT /api/notifications/subscriptionsProp
Type
curl -X PUT https://your-host/api/notifications/subscriptions \
-H "Authorization: Bearer $OPENSHIP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"category":"deploy.failed","channelId":"chan_123","enabled":true}'Org defaults
Defaults seed the subscription grid for new members of the org. Setting a default is admin-only
(notifications:admin); reading them is not.
PUT /api/notifications/defaultsProp
Type
Read the alert feed
GET /api/notifications/deliveries returns your recent deliveries in the active org — this is the history
behind the bell icon. GET /api/notifications/deliveries/unseen-count powers the badge, and POST /api/notifications/deliveries/:id/seen clears one.
GET /api/notifications/deliveriesProp
Type
curl "https://your-host/api/notifications/deliveries?unseen=true&limit=20" \
-H "Authorization: Bearer $OPENSHIP_TOKEN"Errors you might see
400 — invalid channel or missing fields
The kind isn't one of email/webhook/in_app/slack, label is missing, or the config failed its
per-kind check (a malformed email, a non-http(s) webhook URL, or a Slack URL that isn't
https://hooks.slack.com/...). Subscriptions need category, channelId, and a boolean enabled; defaults
need category and a boolean defaultEnabled.
404 on a per-user route
The channel, subscription, or delivery :id doesn't belong to you (or was deleted). List your own with the
matching GET route to get valid ids.