Test Notification Channel

POST /v1/traffic/alerts/test-channel

Delivers one sample notification to a single channel, as you send it (saved or not), 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: sha256=<hex> 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 the channel in the request body and returns an immediate result.

Plan-gated: requires the endpoints:write permission (as create does) AND the metric_alerts entitlement (403 without either). Signing with a saved secret (rule_uuid) also needs endpoints:read, as reading the rule does.

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 sent as X-Ngris-Signature: sha256=<hex> — letting a verifying receiver pass the test exactly as a real fire. Ignored for slack/discord/email. **** — the placeholder reads answer for a saved secret — is not a secret: with a rule_uuid it signs the test with that rule's saved secret (below); without one the test is not sent and the answer is sent: false with an error asking you to re-enter the secret.
rule_uuid string (uuid)
Optional. The saved rule whose secret signs the test, used only when the webhook secret is **** (testing a channel of a rule you are editing). The test is signed with the secret of that rule's channel with the same type and target, else of its channel at channel_index when that one has the same type. With no match, the answer is sent: false with an error asking you to re-enter the secret. A rule that is not in your account answers 404.
channel_index int
Optional, with rule_uuid. The channel's 0-based position in the rule's notify_channels, matched when its URL was edited (no saved channel has the same target).
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 endpoints:write permission (or endpoints:read when rule_uuid is used), or plan doesn't include metric_alerts.
404 Not Found
The webhook secret is **** and rule_uuid names no rule in your account.
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 a saved rule's webhook with its saved secret
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": "****", "rule_uuid": "019c12ab-1cf9-7acd-9ca4-e05a5284d4d8", "channel_index": 1, "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" }
Was this helpful?
Was this helpful?