Test Notification Channel

POST /v1/traffic/alerts/test-channel

Delivers one sample notification to a single, not-yet-saved channel and reports the synchronous pass/fail inline — the "Send test" button on the alert authoring form. Use it to confirm a webhook / Slack / Discord URL or email address actually receives a notification before arming a rule. The channel is validated with the same rules Create Alert enforces on save, then delivered synchronously.

Delivery reuses the exact payload shapes the alert worker produces, so a passing test exercises the real wire contract: webhooks receive a JSON body (HMAC-SHA256-signed as X-Ngris-Signature when a secret is supplied), Slack receives {"text": ...}, Discord receives {"content": ...}, and email is sent via the deployment's SMTP sender. Webhook / Slack / Discord URLs are POSTed through an SSRF-safe client, so internal, loopback, and link-local addresses are rejected.

Unlike the per-rule Test Alert route (which stamps an async test on an existing rule), this endpoint takes an unsaved channel and returns an immediate result.

Plan-gated: requires the traffic-inspector write permission AND the metric_alerts entitlement (403 without either).

Request body

type string Req
Channel type. One of: email, webhook, slack, discord. Any other value returns sent: false with an "unsupported channel type" message.
target string Req
The destination, validated per type: email target must contain @; webhook target must be an https:// URL; slack target is a Slack Incoming Webhook URL (https://hooks.slack.com/...); discord target is a Discord webhook URL (https://discord.com/api/webhooks/...). Leading/trailing whitespace is trimmed.
secret string
Optional, webhook only. When set, the test POST body is HMAC-SHA256-signed and the signature sent as X-Ngris-Signature — letting a verifying receiver pass the test exactly as a real fire. Ignored for slack/discord/email.
name string
Optional context echoed into the sample message body so the recipient can tell which in-progress rule the test came from. Defaults to "Sample alert" when blank.

Response (200 OK)

A delivery outcome — both success and a failed delivery return HTTP 200; the transport succeeded, only the delivery may not have. A transport-level failure (e.g. SSRF block, connection error, non-2xx/3xx from the endpoint) is reported as sent: false with the reason in error.

sent boolean
true if the sample notification was delivered successfully (any 2xx/3xx for webhook/slack/discord; SMTP accepted for email).
error string
Present only when sent is false. Human-readable reason, e.g. a validation message, an SSRF/connection failure, or "endpoint returned HTTP 500: ...".

Errors

400 Bad Request
Invalid JSON body. (A valid body with a bad/unsupported channel returns 200 with sent: false, not a 400.)
401 Unauthorized
Missing auth.
403 Forbidden
No active account, missing the traffic-inspector write permission, or plan doesn't include metric_alerts.
Request — test a signed webhook
curl -X POST "https://api.ngris.com/v1/traffic/alerts/test-channel" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "type": "webhook", "target": "https://hooks.acme.dev/ngris", "secret": "<hmac_secret>", "name": "5xx spike on api" }'
Request — test an email address
curl -X POST "https://api.ngris.com/v1/traffic/alerts/test-channel" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{"type": "email", "target": "ops@acme.dev"}'
Response — delivered
{ "sent": true }
Response — delivery failed (still HTTP 200)
{ "sent": false, "error": "endpoint returned HTTP 500: internal error" }
Iris