Notifications
Configure channels, subscriptions, and alert delivery.
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, Discord, Microsoft Teams, 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.
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.
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, group, label,
description, and defaultEnabled.
The response also carries a groups array ({ id, label }, already in display order) that the categories'
group field points into — deployment, app_health, backups, jobs, domains, members, billing.
The billing group and its categories are omitted outside Openship Cloud, where nothing can fire them.
const result = await ship.notifications.categories();
console.log(result);curl "$OPENSHIP_URL/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, Telegram bot tokens) are stored encrypted and never returned in full.
const result = await ship.notifications.createChannel({
kind: "email",
label: "On-call inbox",
config: {
address: "ops@example.com",
},
});
console.log(result);curl -X POST "$OPENSHIP_URL/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"}}'Prop
Type
The config shape depends on kind:
Prop
Type
Getting a Teams webhook URL
In Teams, create a Workflow with the "When a Teams webhook request is received" trigger
and use the URL it generates. Legacy webhook.office.com connector URLs are also accepted. The
URL is stored encrypted and comes back as webhookUrlConfigured in responses, never in full.
Getting a Telegram bot token and chat id
Message @BotFather → /newbot for the token. A bot can only write to a chat that has already
started it, so send the bot a message (or add it to your group) first. For the chat id, call
https://api.telegram.org/bot<token>/getUpdates and read result[].message.chat.id — group ids
are negative. Editing a Telegram channel without resending botToken keeps the stored one.
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.
const result = await ship.notifications.upsertSubscription({
category: "deploy.failed",
channelId: "chan_123",
enabled: true,
});
console.log(result);curl -X PUT "$OPENSHIP_URL/api/notifications/subscriptions" \
-H "Authorization: Bearer $OPENSHIP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"category":"deploy.failed","channelId":"chan_123","enabled":true}'Prop
Type
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.
const result = await ship.notifications.listDeliveries({
unseen: true,
limit: 20,
});
console.log(result);curl "$OPENSHIP_URL/api/notifications/deliveries?unseen=true&limit=20" \
-H "Authorization: Bearer $OPENSHIP_TOKEN"Prop
Type
Errors
400 — invalid channel or missing fields
The kind isn't one of email/webhook/in_app/slack/discord/msteams/telegram, label
is missing, or the config failed its per-kind check (a malformed email, a non-http(s) webhook
URL, a Slack URL that isn't https://hooks.slack.com/..., a Discord URL that isn't
https://discord.com/api/webhooks/..., a Teams URL that isn't an https Workflow or
webhook.office.com URL, or a Telegram token/chat id that isn't in <id>:<secret> /
numeric-or-@name form). 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.
Operations
| Operation | SDK | REST API |
|---|---|---|
| List supported notification categories and events. | notifications.categories() | GET /api/notifications/categoriesnotifications:read |
| List configured delivery channels. | notifications.listChannels() | GET /api/notifications/channelsnotifications:read |
| Create a delivery channel. | notifications.createChannel(input) | POST /api/notifications/channelsnotifications:write |
| Read notification subscriptions. | notifications.listSubscriptions() | GET /api/notifications/subscriptionsnotifications:read |
| Create or update a subscription. | notifications.upsertSubscription(input) | PUT /api/notifications/subscriptionsnotifications:write |
| Read default notification routing. | notifications.listDefaults() | GET /api/notifications/defaultsnotifications:read |
| Set default notification routing for a category. | notifications.upsertDefault(input) | PUT /api/notifications/defaultsnotifications:admin |
| Read notification delivery history. | notifications.listDeliveries(input?) | GET /api/notifications/deliveriesnotifications:read |
| Count notifications the user has not seen. | notifications.unseenCount() | GET /api/notifications/deliveries/unseen-countnotifications:read |
| Update channel configuration or rotate credentials. | notifications.updateChannel(id, input) | PATCH /api/notifications/channels/:idnotifications:write |
| Test the channel’s delivery configuration. | notifications.testChannel(id) | POST /api/notifications/channels/:id/testnotifications:write |
| Remove a delivery channel. | notifications.removeChannel(id) | DELETE /api/notifications/channels/:idnotifications:write |
| Remove a subscription. | notifications.removeSubscription(id) | DELETE /api/notifications/subscriptions/:idnotifications:write |
| Mark the selected notifications seen. | notifications.markSeen(id) | POST /api/notifications/deliveries/:id/seennotifications:write |