Preview Rule Impact (Blast Radius)

POST /v1/endpoints/{uuid}/policies/preview

Replay the endpoint's recently-captured traffic through a proposed, unsaved rule and get its blast radius before you commit it: how many requests would be newly blocked / throttled / redirected, how many previously-successful requests would break, the distinct client IPs affected, and a verdict. Read-only — no rule is created and no traffic is affected.

It runs the same matcher the live data plane uses, so the preview can't drift from what the rule would actually do. The account and endpoint are resolved from your session and the endpoint's ownership is verified — you can only ever replay your own account's traffic.

Requires request capture to be enabled on the endpoint. Endpoints with capture off, or with no traffic in the window, return an insufficient_data verdict (never a scary number).

Path parameters

uuid string (uuid) Req
The endpoint's uuid from /endpoints.

Request body

rule_kind string Req
One of traffic_policy, firewall, rate_limit.
op string
add (default), modify, or remove. remove reports the traffic a rule was blocking that would be re-admitted.
rule_json object Req
The proposed rule. For traffic_policy: {"rule_type": "...", "phase": "...", "config": {...}} (same shape as create). Only deterministic, replayable rule types are simulated: restrict-ips, rate-limit, redirect, force-https, custom-response, url-rewrite, add-headers, remove-headers; others return an unsimulated count.
window_minutes int
How far back to replay, in minutes (default 120, max 1440). At most 5,000 requests are replayed; a busier window is sampled and the counts scaled (estimated: true).

Response (200 OK)

A DryRunImpact object:

verdict string
safe (tightens only a tiny abuse-only share, or a pure relax), review (nothing alarming, worth a human glance), risky (would cut real successful traffic), low_confidence (too few samples to trust) or insufficient_data (capture off, or no traffic in the window).
verdict_reasons string[]?
Why the verdict was given, in plain words. Omitted when there are none.
window_start, window_end datetime
The replayed window (RFC 3339 UTC).
capture_level string
The endpoint's capture setting: full, metadata, or off (capture disabled — nothing to replay, verdict insufficient_data).
newly_blocked, newly_throttled, newly_redirected, newly_allowed int
Requests whose outcome the change would flip: blocked, rate-limited, redirected, or (for op: "remove") re-admitted.
unchanged int
Requests the change would not affect.
unsimulated int
Requests the rule would act on in a way a replay can't predict — a rule type outside the simulated set above, or a firewall challenge (whether a visitor passes depends on their browser). Never guessed.
affected_successful_reqs int
The important number: requests that were 2xx/3xx that the change would newly stop.
affected_distinct_ips, affected_share_pct int / number
Distinct client IPs affected and the % of sampled traffic affected.
sampled_rows, total_rows, estimated int / bool
How many rows were replayed vs. matched the window; estimated is true when scaled from a sample.
top_ips, top_paths, top_countries array?
Top affected dimensions, each {"key": "...", "count": N}. Each is omitted when empty.

Errors

400
Missing rule_json, unsupported rule_kind, or an invalid rule config.
401 / 404
Not authenticated / endpoint not found in your account.
Request — preview an IP block
curl -X POST "https://api.ngris.com/v1/endpoints/019bb1e4-e785-77d2-9966-c3cff31d0a0e/policies/preview" \ -H "X-API-KEY: <your_api_key>" \ -H "Content-Type: application/json" \ -d '{ "rule_kind": "traffic_policy", "op": "add", "window_minutes": 120, "rule_json": { "rule_type": "restrict-ips", "phase": "on_http_request", "config": {"deny": ["203.0.113.0/24"]} } }'
Response — 200 OK
{ "window_start": "2026-04-29T10:00:00Z", "window_end": "2026-04-29T12:00:00Z", "capture_level": "full", "sampled_rows": 1840, "total_rows": 1840, "estimated": false, "newly_blocked": 57, "newly_throttled": 0, "newly_redirected": 0, "newly_allowed": 0, "unchanged": 1783, "unsimulated": 0, "affected_successful_reqs": 42, "affected_distinct_ips": 5, "affected_share_pct": 3.1, "top_ips": [{"key": "203.0.113.9", "count": 31}], "top_paths": [{"key": "/api/orders", "count": 22}], "verdict": "risky", "verdict_reasons": ["would cut a meaningful number of previously-successful requests"] }
Was this helpful?
Was this helpful?