What fires an alert
Alerts are issue-state-driven, not occurrence-driven:- A new issue opens (first time the fingerprint is seen).
- A resolved issue reoccurs (same fingerprint, new occurrence after the resolve timestamp).
- A performance issue opens because a route crossed its p95 threshold.
The four channel types
Email (BYOD SMTP)
Delivered through your own SMTP credentials — SendGrid, Postmark, AWS SES, Mailgun, or a self-hosted MTA. Credentials are encrypted at rest with Laravel’s
Crypt facade.Slack
Incoming webhook URL from a Slack app. Rich message formatting with issue title, environment, stack-trace excerpt, and a direct link back to the dashboard.
Discord
Channel webhook URL from Discord’s Integrations settings. Same rich embed as Slack, adapted to Discord’s format.
Webhook
Arbitrary HTTPS endpoint. JSON payload, optional HMAC-SHA256 signature in the
X-NightOwl-Signature header for verification.Crypt and decrypted only in-memory when dispatching.
Recipients are specified as a comma-separated list in the channel config. For team-wide alerts, use a distribution list (engineering@…) rather than listing individuals.
Slack
- In Slack, create an app at api.slack.com/apps → Incoming Webhooks → Add New Webhook to Workspace.
- Copy the webhook URL (starts with
https://hooks.slack.com/services/…). - Paste into the Slack channel config in the NightOwl dashboard.
Discord
- In Discord, open Server Settings → Integrations → Webhooks → New Webhook, pick the destination channel, and copy the URL.
- Paste into the Discord channel config.
Webhook
For anything more custom — routing to PagerDuty, OpsGenie, a bespoke router, or your own incident bot — use a webhook channel. The payload shape:hash_equals. Reject any request whose signature doesn’t match — this prevents forged alerts from reaching your downstream system.
Testing a channel
Every channel has a Send test button that dispatches a syntheticissue.opened alert immediately. Use it after setup and after any credential rotation. The test uses the exact same dispatch path as real alerts, so a successful test guarantees real alerts will reach the same destination.
If a test fails, the dashboard shows the specific error (SMTP auth failure, 4xx response from a webhook, invalid Slack URL) rather than a generic message.
Toggling and disabling
Channels can be disabled without deleting. A disabled channel is skipped by the dispatcher but preserves its config and history — useful for silencing a channel during maintenance without losing the webhook URL or SMTP credentials.Under the hood
Alert dispatch goes through theChannelDispatcherFactory on the backend, which resolves one of four implementations (EmailDispatcher, SlackDispatcher, DiscordDispatcher, WebhookDispatcher). The nightowl:notify-issues artisan command runs on a schedule, fans out to every enabled channel per app, and tolerates individual channel failures — one broken webhook never blocks Slack from firing.