Alert channels decide where NightOwl tells you something needs attention. They’re configured per app in Settings → Alert Channels, and every enabled channel fires for the same set of triggers — so you can mix a noisy Slack firehose with a quiet email summary without re-wiring your issue workflow.Documentation Index
Fetch the complete documentation index at: https://docs.usenightowl.com/llms.txt
Use this file to discover all available pages before exploring further.
What fires an alert
Alerts are issue-lifecycle events, not per-occurrence:| Event | When it fires |
|---|---|
issue.new | A brand-new fingerprint is seen for the first time. Emitted directly from the agent’s drain worker. |
issue.reopened | A previously resolved issue’s fingerprint recurs (auto-reopen by the agent), or a user manually reopens an ignored/resolved issue from the dashboard, MCP, or API. The agent flips status back to open and appends a status_changed activity row with actor_type='agent'. |
issue.resolved | A user resolves the issue from the dashboard, MCP, or API. |
issue.ignored | A user ignores the issue from the dashboard, MCP, or API. |
resolved is different: a recurrence is treated as a regression, so the agent auto-flips the status back to open and fires issue.reopened. Use NIGHTOWL_REOPEN_COOLDOWN_HOURS (see Configuration → Issue lifecycle) to require N hours of silence before the auto-reopen fires — useful for flapping issues.
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 must be existing team members of the app — the dashboard rejects addresses that aren’t in the team. If you need to notify an external address (an on-call alias, a PagerDuty email trigger, a Slack email relay), add that address as an invited team member first, or route through a Slack/Discord/webhook channel instead.
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. Every request isPOST application/json with this shape:
null. Notes:
app_idandview_urlcome from the agent’sNIGHTOWL_APP_IDenv var. When set, both fields embed the connected-app ID and a direct link to the issue page. When unset,app_idisnullandview_urlfalls back to the generic/dashboardroot. See agent configuration.messageis truncated to 200 characters with an ellipsis.- Exception events populate
handled,location,php_version,laravel_version. Performance events populateroute,threshold_ms,duration_ms, and usesubtypeto distinguishroute/job/command/query/ etc. The unused set isnull. statusandpriorityreflect the row’s current dashboard state at dispatch time. For a brand-new fingerprint,statusis"open"(the DB default) andpriorityisnulluntil a user assigns one. When a previously-resolved issue recurs, the agent flipsstatusback to"open"before dispatchingissue.reopened, so receivers see the new state immediately.- Field
typeis always"exception"or"performance".
Signing
If you set a signing secret on the channel, NightOwl signs each request with HMAC-SHA256 over the raw JSON body:sha256= prefix. Verify by recomputing the digest with your secret and the raw body (before any JSON parsing), then comparing with hash_equals. Reject any request whose signature doesn’t match.
Testing a channel
Every channel has a Send test button. The test payload is deliberately minimal — just enough to prove the transport works — so don’t expect it to match the real issue schema:- Webhook:
{ "event": "test", "app": "<app name>", "timestamp": "<iso8601>" }. Noissueobject. - Slack / Discord: a plain “Test Notification” message in the destination channel.
- Email: a short HTML email with the subject
[<app-name>] NightOwl Test Notification.
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
Issue alerts come from two places:issue.newandissue.reopened(auto-reopen) — the NightOwl agent’s drain worker dispatches directly from the customer app:issue.newafter a brand-new fingerprint is upserted,issue.reopenedafter aresolvedfingerprint recurs (subject toNIGHTOWL_REOPEN_COOLDOWN_HOURS). No round-trip through NightOwl’s API.issue.resolved/issue.ignored/issue.reopened(manual) — the NightOwl API dispatches via a queuedDispatchIssueAlertsJobwhenever a user changes status from the dashboard, MCP, or the API.issue.reopenedtherefore has two emission paths (agent auto-reopen and user-initiated reopen), but the payload shape is identical.
X-NightOwl-Signature signing, and respect per-channel notify_events filtering. Dispatch is isolated per channel with a short timeout — one broken webhook never blocks Slack or email from firing.