Notification channels
A channel is a delivery target owned by a project. Every alert rule in the project (uptime rules, error alert rules) points at one channel, so you set up Slack or PagerDuty once and reuse it.
Creating a channel
Dashboard: Monitor → project → Channels. Pick a type, give it a name and fill in the fields. The list has Test, Rename, enable/disable and delete.
API: through the kamuhub front door with an access key that has the
status.manage capability.
curl -X POST https://app.kamuhub.com/api/status/projects/<project-id>/channels \
-H "Authorization: Bearer $KAMU_ACCESS_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Ops Slack", "type": "slack",
"config": {"webhookUrl": "https://hooks.slack.com/services/..."}}'
PATCH /api/status/channels/<id> changes name, config (replaced whole) or
enabled; DELETE removes it. There are no kamu CLI commands for channels.
The API checks the type, not the config. A missing field only shows up when
the first alert fails to send, so press Test after creating a channel.
Test sends a real message for Slack, Discord, Google Chat, ntfy, Telegram and
webhook channels. For the other types it only answers "configured correctly"
without contacting the service.
Channel types
Fourteen types. The dashboard form covers eleven; matrix, twilio_sms and
twilio_call are API-only. Optional fields in the table are API-only as well;
the dashboard sends only the required ones.
type | Required config | Optional config | Sends |
|---|---|---|---|
email | addresses (array of strings) | One mail per address, subject [kamustatus] <name> - DOWN or - UP | |
slack | webhookUrl (incoming webhook) | {"text": message} | |
discord | webhookUrl | {"content": message} | |
telegram | botToken, chatId | Bot API sendMessage, parse_mode: HTML | |
teams | webhookUrl | Adaptive Card with the message as one text block | |
google_chat | webhookUrl | {"text": message} | |
pagerduty | routingKey (Events API v2) | severity (default critical) | trigger on down, resolve on up |
opsgenie | apiKey | priority (default P1) | Creates an alert on down, closes it on up |
pushover | userKey, appToken | priority (default 1) | Message with sound siren |
ntfy | topic | serverUrl (default https://ntfy.sh), priority (default 4) | POST to <serverUrl>/<topic>, title = monitor name |
matrix | homeserver, roomId, accessToken | m.text message to the room | |
twilio_sms | accountSid, authToken, from, to | SMS with the message | |
twilio_call | accountSid, authToken, from, to | Voice call reading out <name> is down. or is up. | |
webhook | url | method (default POST), headers (object) | JSON payload below |
PagerDuty and OpsGenie dedupe on kamustatus-<monitorId>, so a recovery
resolves the incident the outage opened. For error alerts the id is
error:<ruleId>, one incident per rule.
Telegram parses the text as HTML, so a monitor name containing < or & can
make Telegram reject the message.
Message text
All chat, SMS and push types send one line of text:
| Event | Text |
|---|---|
| Monitor down | [DOWN] <monitor>: <error> (<region>) |
| Monitor recovered | [UP] <monitor> is back online (<region>) |
| Certificate expiring | [SSL] <monitor>: certificate expires in N day(s) |
| Domain expiring | [DOMAIN] <monitor>: registration expires in N day(s) |
| Error alert | Text built by the rule, see error alert rules |
Webhook payload
A webhook channel receives Content-Type: application/json plus any
configured headers:
{
"monitor": "API",
"monitorId": "V1StGXR8_Z5jdHi6B-myT",
"status": "down",
"error": "Timeout",
"region": "arn",
"timestamp": "2026-10-27T08:30:00.000Z"
}
-
statusisdownorup. -
errorandregionare left out when unknown. A recovery has noerror, and a heartbeat alert has noregion. -
Certificate and domain expiry warnings also arrive as
status: "down"with noerror. The webhook body does not carry the warning text. Use another channel type if you need to tell them apart. -
Error alerts add three fields.
monitoris the property name andmonitorIdiserror:<ruleId>:{
"monitor": "myapp",
"monitorId": "error:abc123",
"status": "down",
"timestamp": "2026-10-27T08:30:00.000Z",
"propertyId": "p_123",
"property": "myapp",
"message": "..."
}
A test send from the dashboard posts {"test": true, "message": "This is a test notification from kamustatus"} instead.
Delivery
- Each send is recorded in the monitor's Alert history with status
triggered,resolved,cert_expiring,domain_expiringorfailed. - A send counts as failed only when the request itself fails (DNS, connection, timeout) or a required field is missing. An HTTP error status from the receiver is not checked. Failed sends are not retried. While the monitor stays down the rule notifies again every 10 minutes.
- Expiry warnings go out at most once a day per rule.
- Disabling a channel in the dashboard does not stop rules from using it. Remove or disable the alert rules instead.