Notification Channels
Control Zero can send governance alerts to multiple channels simultaneously. Configure which channels receive alerts and at what severity level from the Settings > Notifications page.
Available Channels
Slack
Post alerts to a Slack channel via an incoming webhook.
Configuration fields:
- Webhook URL -- Create an incoming webhook in your Slack workspace at api.slack.com/messaging/webhooks and paste the URL here.
- Channel -- The target channel (e.g.
#alerts). This is informational; the webhook determines the actual destination.
Alerts are sent as rich Slack attachments with color-coded severity.
Telegram
Receive alerts via a Telegram bot.
Configuration fields:
- Bot Token -- Create a bot via @BotFather and use the token it provides.
- Chat ID -- The chat or group ID where alerts should be sent. Use @userinfobot to find your chat ID.
Discord
Post alerts to a Discord channel via webhook. Alerts are sent as rich embeds with color-coded severity.
Configuration fields:
- Webhook URL -- Create a webhook in your Discord server under Channel Settings > Integrations > Webhooks.
Discord rate limits apply (5 requests per 2 seconds per webhook). The system respects these limits.
Email (via Resend)
Send alert emails to one or more recipients via the Resend API.
Configuration fields:
- Email Addresses -- Comma-separated list of recipient email addresses (e.g.
ops@yourcompany.com, security@yourcompany.com).
Emails are sent from alerts@controlzero.ai with HTML formatting. The subject line includes the alert severity level.
Server-side requirement: The RESEND_API_KEY environment variable must be set on the backend for email delivery to work.
Webhook (Generic)
POST a JSON payload to any HTTP endpoint. Useful for integrating with custom alerting systems, PagerDuty, Opsgenie, or any service that accepts webhooks.
Configuration fields:
- Endpoint URL -- The full URL where alert payloads will be POSTed.
- Signing Secret (optional) -- If provided, every request includes an
X-CZ-Signatureheader containing an HMAC-SHA256 hex digest of the request body, computed with this secret.
Payload format:
{
"event": "alert",
"level": "error",
"title": "Policy Violation Detected",
"message": "Agent attempted to call blocked tool: execute_shell",
"service": "control-zero",
"timestamp": "2026-04-11T14:30:00Z",
"metadata": {
"organization_id": "...",
"project_id": "..."
}
}
Signature verification (pseudocode):
import hmac, hashlib
expected = hmac.new(
key=signing_secret.encode(),
msg=request_body,
digestmod=hashlib.sha256
).hexdigest()
assert expected == request.headers["X-CZ-Signature"]
Webhook requests have a 5-second timeout and do not retry on failure.
Alert Levels
Each channel can be configured with a minimum alert level. Only alerts at or above the configured level are sent to that channel.
| Level | Description |
|---|---|
| Debug | Verbose diagnostic events |
| Info | Routine operational events |
| Warning | Conditions that may require attention |
| Error | Failures that need investigation |
| Critical | Urgent issues requiring immediate action |
Event Scoping
Beyond the minimum alert level, each channel can be scoped to specific event types. On each channel card, select one or more events under Events to route only those events to that channel. Leave every event unselected to receive all event types (the default).
| Event | Fires when |
|---|---|
policy.denied | A burst of policy denials crosses the spike threshold |
security.hook_tamper | An enforcement hook's integrity check fails on a host |
security.credential_leak | The credential scanner detects a secret in agent I/O |
usage.limit_reached | The organization reaches its monthly action quota |
Human-in-the-loop approval events (hitl.approval.requested, .approved,
.denied, .expired) are also deliverable and can be scoped the same way.
Policy Denial Spike Threshold
A single policy denial is normal governance, not an incident -- so the
policy.denied alert fires only when a spike of denials crosses a threshold
within a rolling window, rather than on every deny. This keeps a noisy day from
paging you on each blocked call, and aligns with the common alerting practice of
requiring a condition to persist before firing.
The threshold is configurable per organization on the Settings > Alerts page:
- Denials (count) -- how many denials must accumulate within the window.
- Window (minutes) -- the rolling window over which denials are counted.
Leave either field blank to use the platform default (10 denials in 5 minutes). Once a spike fires it does not fire again until the window clears, so a sustained denial stream pages once per window rather than continuously.
Tier Availability
| Channel | Free | Solo | Teams |
|---|---|---|---|
| Telegram | Yes | Yes | Yes |
| Slack | -- | Yes | Yes |
| -- | Yes | Yes | |
| Discord | -- | Yes | Yes |
| Webhook | -- | Yes | Yes |
Testing Notifications
Each channel has a "Send test notification" button in the settings page. Use this to verify your configuration is correct before relying on it for production alerts.