Create Metric Alert Rule

POST /v1/traffic/alerts

Creates a threshold rule the evaluator worker will check on each cycle. A rule combines a metric, an operator, a numeric threshold, and a sliding window. When the observed value crosses the threshold the rule transitions to firing and the worker sends one notification per configured channel — subject to cooldown_seconds to suppress repeats.

Endpoint scope is optional: omit endpoint_uuid to evaluate across every endpoint owned by the caller; supply one to scope the metric to a single endpoint.

Plan-gated: requires the metric_alerts entitlement (403 without it).

Request body

name string Req
Free-form label. Max 255 chars.
description string
Optional. Free-form context (e.g. who gets paged, what to do).
metric string Req
One of: request_rate (req/s), error_rate_pct (% of 4xx+5xx), p95_latency_ms, p99_latency_ms, bandwidth_in_bps, bandwidth_out_bps.
operator string Req
Comparison against the threshold. One of: >, >=, <, <=.
threshold number Req
Threshold value in the metric's native unit (req/s, percent, ms, bytes/s).
window_seconds int Req
Aggregation window. Must be between 60 and 86400 (1 minute to 24 hours).
endpoint_uuid string (uuid)
Optional. Scope the metric to a single endpoint. Omit for account-wide.
cooldown_seconds int
Minimum gap between consecutive notifications while the rule stays firing. 0–86400.
enabled boolean
Defaults to true. Disabled rules are persisted but skipped by the evaluator.
notify_channels array<Channel> Req
At least one channel. Each channel has type + target (and optional secret). Supported types: email (target must contain @); webhook (target must be an https:// URL; may include a secret the worker uses to HMAC-SHA256-sign the POST body as X-Ngris-Signature); slack (target is a Slack Incoming Webhook https://hooks.slack.com/... URL); discord (target is a Discord webhook https://discord.com/api/webhooks/... URL). Slack/Discord carry no secret — the URL itself is the credential.

Response (201 Created)

The newly-created rule with assigned uuid and resolved endpoint_id. status starts at unknown until the first evaluator cycle.

Errors

400 Bad Request
Validation failure: missing name/metric/operator, out-of-range window or cooldown, unknown metric, no notification channels, or an invalid channel target.
400 endpoint not found
The supplied endpoint_uuid doesn't match an endpoint in the caller's tenant. Returned as a 400 field-error keyed to endpoint_uuid, not a 404.
401 Unauthorized
Missing auth.
403 Forbidden
Plan doesn't include metric_alerts.
Request — error-rate alert
curl -X POST "https://api.ngris.com/v1/traffic/alerts" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "name": "5xx spike on api", "description": "Page on-call when api endpoint errors exceed 1%", "metric": "error_rate_pct", "operator": ">", "threshold": 1.0, "window_seconds": 300, "endpoint_uuid": "<endpoint_uuid>", "cooldown_seconds": 600, "notify_channels": [ {"type": "email", "target": "ops@acme.dev"}, {"type": "webhook", "target": "https://hooks.acme.dev/ngris", "secret": "<hmac_secret>"} ] }'
Request — account-wide p99 latency
curl -X POST "https://api.ngris.com/v1/traffic/alerts" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "name": "p99 latency over 500ms", "metric": "p99_latency_ms", "operator": ">", "threshold": 500, "window_seconds": 300, "cooldown_seconds": 900, "notify_channels": [{"type": "email", "target": "sre@acme.dev"}] }'
Response — 201 Created
{ "uuid": "0d3e9f8a-2b5c-4a7d-9f1a-1a2b3c4d5e6f", "name": "5xx spike on api", "metric": "error_rate_pct", "operator": ">", "threshold": 1.0, "window_seconds": 300, "endpoint_id": 99, "cooldown_seconds": 600, "enabled": true, "status": "unknown", "notify_channels": [ {"type": "email", "target": "ops@acme.dev"}, {"type": "webhook", "target": "https://hooks.acme.dev/ngris"} ], "created_at": "2026-05-21T12:00:00Z", "updated_at": "2026-05-21T12:00:00Z" }
Iris