Uptimeify Docs
Notification channels

Create Notification Channel

Creates a new notification channel. Secrets in config are encrypted server-side.

POST /api/notification-channels

Request Body

FieldTypeRequiredDefaultDescription
typestringYes-Channel type, one of the list below (anything else is a 400)
namestringYes-Display name, 1 to 100 characters
configobject|stringYes-Channel configuration (see type table). Pass as JSON object or JSON string of at most 20 kB.
organizationIdnumberNofrom sessionOrganization scope
customerIdnumberNonullCustomer scope
websiteIdnumberNonullWebsite scope. Requires sourceChannelId.
sourceChannelIdnumber|stringConditionalnullParent channel ID for website overrides
categorystringNodirectdirect or integration
delaySecondsnumberNo0Delay before sending alert, integer 0 to 86400
conditionsobject|stringNonullAlert conditions (e.g., {"onlyFullService": true, "minIncidentDuration": 300}), JSON object or JSON string of at most 20 kB
isActivebooleanNotrueWhether 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 Request with data.code invalidRequestBody when an email or sms channel carries a recipient the delivery path could never accept. Checked for type: email in config.to and config.email, and for type: sms in config.to, config.phoneNumbers and config.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 form Name <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. Only email and sms are checked; on every other type these keys mean something else.
  • 400 Bad Request with data.code invalidRequestBody when a target URL in config is not a parseable http/https URL, or points at a private, loopback or link-local address, at localhost, at a reserved private-use name (.local, .internal, .home.arpa) or at a single-label host. Every top-level config string starting with http:// or https:// 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 Request when the body fails validation: unknown type, name missing or over 100 characters, config or conditions that is not a JSON object (or a string that does not parse to one), delaySeconds out of range
  • 401 Unauthorized when not authenticated
  • 403 Forbidden when creating channels for an organization you cannot write to
  • 409 Conflict when a duplicate website-level override already exists
  • 409 Conflict with data.code notificationChannelRecipientExists when an ACTIVE channel of the same type in the same scope already delivers to the same recipient. The response carries the existing channel in data.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.

On this page