Skip to main content

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-Signature header 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.

LevelDescription
DebugVerbose diagnostic events
InfoRoutine operational events
WarningConditions that may require attention
ErrorFailures that need investigation
CriticalUrgent 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).

EventFires when
policy.deniedA burst of policy denials crosses the spike threshold
security.hook_tamperAn enforcement hook's integrity check fails on a host
security.credential_leakThe credential scanner detects a secret in agent I/O
usage.limit_reachedThe 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). Self-managed operators can change that default with the ALERT_DENY_SPIKE_COUNT and ALERT_DENY_SPIKE_WINDOW_SECONDS environment variables. 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

ChannelFreeSoloTeams
TelegramYesYesYes
Slack--YesYes
Email--YesYes
Discord--YesYes
Webhook--YesYes

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.