Notification Channels API
Notification channels define where alerts are sent when route failures, recoveries, or other events occur.
Warning
Paid plans only
Every endpoint on this page is gated on the notification_channels feature, which is enabled on
Starter, Pro and Business. On the Free plan every request returns 403:
{
"error": "Notification channels are only available on paid plans. Please upgrade to configure Slack, email, or webhook notifications."
}Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/notification-channels | List channels |
| POST | /api/notification-channels | Create channel |
| GET | /api/notification-channels/{id} | Get channel |
| PATCH | /api/notification-channels/{id} | Update channel |
| DELETE | /api/notification-channels/{id} | Delete channel |
| POST | /api/notification-channels/{id}/routes | Link a route to the channel |
| GET | /api/notification-channels/{id}/routes | List the channel's linked routes |
| PATCH | /api/notification-channels/{id}/routes/{routeId} | Change a link's triggers |
| DELETE | /api/notification-channels/{id}/routes/{routeId} | Unlink a route |
| POST | /api/notification-channels/{id}/test | Send test notification |
Reading requires any member role. Every other method requires admin or owner; the two
DELETEs also require an API key with the delete scope.
A channel only sends anything once it is linked to at least one route — see Link a Route.
Notification Channel Object
{
"id": "6f2e0f1a-4c8e-4c2a-9a1c-2f4b7f0d5e31",
"organizationId": "b1d4c8e2-1f3a-4f6d-9c0b-7a5e2d9f1c34",
"name": "Slack #alerts",
"type": "slack",
"config": {
"webhookUrl": "https://hooks.slack.com/***"
},
"isActive": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}The active flag is isActive. There is no enabled field and no lastNotifiedAt field.
Info
Secrets are masked in responses
GET responses never repeat what you sent in config:
slack,teams,discord—webhookUrlis reduced tohttps://host/***pagerduty—routingKeykeeps only its last 4 characterswebhook—urlis left as sent,urlMaskedis added alongside it, andsecretis removed and replaced byhasSecret: true | false
A create response is the exception: it echoes the config you sent, unmasked.
Channel Types
Slack
{
"type": "slack",
"config": {
"webhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
"channel": "#ops-alerts"
}
}webhookUrl must start with https://hooks.slack.com/. channel is optional and overrides the
channel the webhook was created against.
Microsoft Teams
{
"type": "teams",
"config": {
"webhookUrl": "https://yourtenant.webhook.office.com/webhookb2/..."
}
}webhookUrl must contain .webhook.office.com/. A legacy https://outlook.office.com/webhook/...
connector URL is rejected with 400.
Discord
{
"type": "discord",
"config": {
"webhookUrl": "https://discord.com/api/webhooks/...",
"username": "Hookbase",
"avatarUrl": "https://hookbase.app/logo.png"
}
}webhookUrl must start with https://discord.com/api/webhooks/.
PagerDuty
{
"type": "pagerduty",
"config": {
"routingKey": "R02ABCDEFGHIJKLMNOPQRSTUVWXYZ012",
"severity": "critical",
"customService": "Hookbase Webhooks"
}
}routingKey must be exactly 32 characters — an Events API v2 integration key. severity is one of
critical, error, warning, info. customService sets the component on the incident and
defaults to Hookbase.
{
"type": "email",
"config": {
"emails": ["alerts@yourapp.com", "oncall@yourapp.com"]
}
}The key is emails — a non-empty array. addresses is not read and is rejected by config
validation.
Webhook
{
"type": "webhook",
"config": {
"url": "https://yourapp.com/hookbase-alerts",
"method": "POST",
"headers": {
"Authorization": "Bearer your-secret"
},
"secret": "your-signing-secret"
}
}url is required and must start with http. method is POST (the default) or PUT. secret is
the HMAC-SHA256 signing key; when it is set, Hookbase sends
X-Hookbase-Signature: sha256=<hex digest of the body>. hmacSecret is accepted as an alias and
stored as secret. Responses never include the secret, only hasSecret. On PATCH, omit it to keep
the current secret, or send null or "" to remove it.
The body is the payload wrapper:
{
"type": "route_failure",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"type": "route_failure",
"routeId": "route_123",
"routeName": "GitHub → Slack",
"sourceName": "GitHub",
"destinationName": "Slack relay",
"destinationUrl": "https://example.com/webhook",
"errorMessage": "Connection timeout",
"consecutiveFailures": 5,
"eventId": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbb0002",
"dashboardUrl": "https://www.hookbase.app/dashboard"
}
}List Channels
GET /api/notification-channelsExample
curl https://api.hookbase.app/api/notification-channels \
-H "Authorization: Bearer whr_your_api_key"Response
Newest first. Not paginated.
{
"channels": [
{
"id": "6f2e0f1a-4c8e-4c2a-9a1c-2f4b7f0d5e31",
"organizationId": "b1d4c8e2-1f3a-4f6d-9c0b-7a5e2d9f1c34",
"name": "Slack #alerts",
"type": "slack",
"config": { "webhookUrl": "https://hooks.slack.com/***" },
"isActive": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
]
}Create Channel
POST /api/notification-channelsRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name, 1-100 characters |
| type | string | Yes | One of email, slack, webhook, teams, pagerduty, discord |
| config | object | Yes | Type-specific configuration, validated per type |
Those three keys are the whole schema, and it is strict: any other key — including isActive —
returns 400. A channel is created active; use PATCH to deactivate it.
Example
curl -X POST https://api.hookbase.app/api/notification-channels \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Slack #alerts",
"type": "slack",
"config": {
"webhookUrl": "https://hooks.slack.com/services/T.../B.../xxx"
}
}'Response
200 OK, with the config echoed back as sent:
{
"channel": {
"id": "6f2e0f1a-4c8e-4c2a-9a1c-2f4b7f0d5e31",
"organizationId": "b1d4c8e2-1f3a-4f6d-9c0b-7a5e2d9f1c34",
"name": "Slack #alerts",
"type": "slack",
"config": {
"webhookUrl": "https://hooks.slack.com/services/T.../B.../xxx"
},
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}Get Channel
GET /api/notification-channels/{id}Returns { "channel": { ... } } — the full channel object, with config masked as described above.
Update Channel
PATCH /api/notification-channels/{id}Request Body
| Field | Type | Description |
|---|---|---|
| name | string | Display name, 1-100 characters |
| config | object | Replaces the whole config; re-validated against the channel's type |
| isActive | boolean | Whether the channel sends anything |
Strict, like create: any other key returns 400. type cannot be changed — create a new channel
instead. config is replaced wholesale rather than merged, so send every key the type needs,
including the secret.
Response
{ "success": true }The updated channel is not returned; GET it if you need to read it back.
Delete Channel
DELETE /api/notification-channels/{id}Requires the delete scope. Linked routes are unlinked by the cascade.
Response
200 OK:
{ "success": true }Link a Route
A channel sends nothing until a route is linked to it.
POST /api/notification-channels/{id}/routesRequest Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| routeId | string (UUID) | Yes | — | The route to link |
| notifyOnFailure | boolean | No | true | Alert when consecutive failures cross the route's threshold |
| notifyOnSuccess | boolean | No | false | Alert on every successful delivery |
| notifyOnRecovery | boolean | No | true | Alert when a failing route succeeds again |
| notifyOnCircuitOpen | boolean | No | true | Alert when the circuit breaker trips |
Example
curl -X POST https://api.hookbase.app/api/notification-channels/6f2e0f1a-4c8e-4c2a-9a1c-2f4b7f0d5e31/routes \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"routeId": "9c1b7d3e-5a2f-4b8c-8d6e-1f0a3c5b7d92",
"notifyOnFailure": true,
"notifyOnRecovery": true
}'Response
{ "success": true }Linking the same route twice returns 400 with
{ "error": "Route is already linked to this channel" }.
List Linked Routes
GET /api/notification-channels/{id}/routesResponse
{
"routes": [
{
"routeId": "9c1b7d3e-5a2f-4b8c-8d6e-1f0a3c5b7d92",
"routeName": "GitHub → Slack",
"notifyOnFailure": true,
"notifyOnSuccess": false,
"notifyOnRecovery": true,
"notifyOnCircuitOpen": true
}
]
}Update a Link
PATCH /api/notification-channels/{id}/routes/{routeId}Takes any of notifyOnFailure, notifyOnSuccess, notifyOnRecovery, notifyOnCircuitOpen, and
nothing else. Omitted triggers keep their current value. Returns { "success": true }, or 404
with { "error": "Route is not linked to this channel" }.
Unlink a Route
DELETE /api/notification-channels/{id}/routes/{routeId}Requires the delete scope. Returns { "success": true }, and does so whether or not the link
existed.
Test Channel
Send a test notification to verify the channel is configured correctly. The test does not need a linked route, and it is sent even when the channel is inactive.
POST /api/notification-channels/{id}/testThe test payload is a route_failure for a placeholder route named "Test Route", so what arrives
looks like a real failure alert.
Example
curl -X POST https://api.hookbase.app/api/notification-channels/6f2e0f1a-4c8e-4c2a-9a1c-2f4b7f0d5e31/test \
-H "Authorization: Bearer whr_your_api_key"Response
{
"success": true,
"message": "Test notification sent successfully"
}Error Response
400, with the provider's own failure passed through:
{
"success": false,
"error": "Webhook error: 403 invalid_token"
}Error Responses
400 Bad Request
A schema failure — an unknown key, a missing name, a type that is not one of the six — reports
the field:
{
"error": "Invalid input",
"details": {
"formErrors": [],
"fieldErrors": { "type": ["Invalid enum value..."] }
}
}A config failure reports the rule that was broken:
{ "error": "webhookUrl must be a valid Slack webhook URL" }403 Forbidden
The plan does not include notification channels, or the caller is not an admin or owner.
404 Not Found
{ "error": "Notification channel not found" }Related
- Notification Channels Guide — Setup and usage patterns
- Routes API — Configure route notifications