Create Notification Channel
Creates a new notification channel. Secrets in config are encrypted server-side.
POST /api/notification-channels
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | string | Yes | - | Channel type, one of the list below (anything else is a 400) |
name | string | Yes | - | Display name, 1 to 100 characters |
config | object|string | Yes | - | Channel configuration (see type table). Pass as JSON object or JSON string of at most 20 kB. |
organizationId | number | No | from session | Organization scope |
customerId | number | No | null | Customer scope |
websiteId | number | No | null | Website scope. Requires sourceChannelId. |
sourceChannelId | number|string | Conditional | null | Parent channel ID for website overrides |
category | string | No | direct | direct or integration |
delaySeconds | number | No | 0 | Delay before sending alert, integer 0 to 86400 |
conditions | object|string | No | null | Alert conditions (e.g., {"onlyFullService": true, "minIncidentDuration": 300}), JSON object or JSON string of at most 20 kB |
isActive | boolean | No | true | Whether the channel is active |
delaySeconds runs on its own timer for this channel, starting from the moment this channel
would notify, independent of any other channel on the same alert. If a maintenance window starts
before that time elapses, or the incident is already resolved by then, nothing is delivered on
this channel.
priority has been removed from channel configuration. A request that still sends it is
accepted and the field is simply ignored, so existing integrations keep working unchanged.
Channel types
Direct (category: "direct"): email, sms, webhook.
Integrations (category: "integration"): incident_management, slack, discord, teams, pagerduty, opsgenie, allquiet, telegram, googlechat, mattermost, rocketchat, matrix, lark, dingtalk, wecom, ilert, grafanaoncall, squadcast, incidentio, pushover, ntfy, gotify, jira, github, gitlab, linear, servicenow.
The config fields depend on the type: see Integrations for what each channel needs. You can validate a channel before saving with the Test Notification Channel endpoint.
incident_management
Uptimeify's own Incident Management is an integration channel like any other, with two differences:
- It takes an empty
config({}). There is no endpoint and no credential, confirmed outages are delivered internally. - It is not testable: the Test Notification Channel endpoint does not support this type.
What the channel controls is which monitors page through Incident Management. Its scope (customerId / websiteId, both optional, omit both for the whole organization) and its allowedPackageTypes decide whose confirmed outages become IM incidents; everything else stays on classic notifications. A monitoring outage only reaches Incident Management when a matching, active channel exists and the customer's package has integration alerts enabled.
Example (cURL): Email channel
curl -X POST "$BASE_URL/api/notification-channels" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "email",
"name": "Ops Email",
"config": { "email": "ops@deinkunde.com", "to": "Ops Team <ops@deinkunde.com>" },
"organizationId": 1
}'Example (cURL): Slack channel
curl -X POST "$BASE_URL/api/notification-channels" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "slack",
"name": "Alerts Slack Channel",
"config": { "webhookUrl": "https://hooks.slack.com/services/T00/B00/xxx" },
"organizationId": 1
}'Example (cURL): Webhook channel
curl -X POST "$BASE_URL/api/notification-channels" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "webhook",
"name": "Custom Webhook",
"config": {
"url": "https://deinkunde.com/webhook",
"method": "POST",
"headers": { "X-Custom-Header": "value" },
"bodyTemplate": "{\"text\": \"{{websiteName}} is {{status}}\"}",
"timeout": 30,
"retryAttempts": 3,
"retryDelay": 60,
"expectedStatusCodes": "200,201,204"
},
"organizationId": 1
}'Common errors
400 Bad Requestwithdata.codeinvalidRequestBodywhen anemailorsmschannel carries a recipient the delivery path could never accept. Checked fortype: emailinconfig.toandconfig.email, and fortype: smsinconfig.to,config.phoneNumbersandconfig.phoneNumber; a comma-separated string and an array are both read as a list and every entry is checked, and the message names the first offending value. An email recipient needs exactly one@with something on both sides and no spaces, the formName <address>is allowed; an SMS recipient must be E.164, a+, a digit 1-9, then 7 to 14 more digits, with no spaces, dashes or brackets. These are the delivery path's own rules, so a value refused here could never have received an alert. A channel with NO recipient of its own stays valid: delivery then falls back to the customer's or the organization's stored address. Onlyemailandsmsare checked; on every other type these keys mean something else.400 Bad Requestwithdata.codeinvalidRequestBodywhen a target URL inconfigis not a parseablehttp/httpsURL, or points at a private, loopback or link-local address, atlocalhost, at a reserved private-use name (.local,.internal,.home.arpa) or at a single-label host. Every top-levelconfigstring starting withhttp://orhttps://counts as a target URL; fields carrying message content (bodyTemplate,webhookBodyTemplate,headers) are exempt, a link into your own network is legitimate there. The check does not resolve DNS, so a public name that only later resolves privately is stopped at delivery time instead.400 Bad Requestwhen the body fails validation: unknowntype,namemissing or over 100 characters,configorconditionsthat is not a JSON object (or a string that does not parse to one),delaySecondsout of range401 Unauthorizedwhen not authenticated403 Forbiddenwhen creating channels for an organization you cannot write to409 Conflictwhen a duplicate website-level override already exists409 Conflictwithdata.codenotificationChannelRecipientExistswhen an ACTIVE channel of the same type in the same scope already delivers to the same recipient. The response carries the existing channel indata.channelId. Repeating a create call whose response you never saw therefore returns this error instead of a second channel; an inactive channel never blocks a new one, and names are not checked, only the recipient.
Response
Returns the created notification channel object. See Error Codes for error responses.
Permissions
Organization admins may write channels at any level. A customer-scoped login (role readonly)
may create, update and delete channels bound to one of ITS OWN customers: that is what the
integrations page in the dashboard offers the role, and until 04.09.2026 the API refused it with
a bare 403.
Two limits remain. An ORGANIZATION-level channel, one carrying no customerId, stays
admin-only. And a channel of a customer outside the caller's scope is refused, as before. The
roles editor and responder are not writers here.
Notification Channels
Manage how and where alerts are delivered. Channels can be organization-level (default for all customers), customer-level (override for a specific customer), or website-level (override for a specific monitor).
Delete Notification Channel
Deletes a notification channel. If the channel was an org-level default, organization defaults are automatically synced.