Add Rule to Traffic Policy
Append a rule to an existing traffic policy. A rule has a phase (when it runs), a rule_type (what it does), an optional CEL expression (a filter — when it should fire), and a config object whose schema depends on the rule type.
Per-rule-type plan gating runs on this endpoint: even if your plan allows traffic policies, individual rule types (e.g. cache-response, jwt-validation) may need higher-tier plans — each rule type is gated by its own traffic_rule:<rule_type> entitlement (a 403 is returned when the plan lacks it). Update calls don't re-check this per-type gate so admins can keep editing rules of types they no longer have access to.
Body is strict-decoded + the rule's config is validated against a per-type schema and run through an AI safety check (for rule types that serve user-controlled content).
Path parameters
Request body
on_tcp_connect, on_udp_connect, on_http_request, on_http_response.restrict-ips, rate-limit, add-headers, remove-headers, url-rewrite, redirect, custom-response, require-client-cert, jwt-validation, verify-webhook, force-https, webhook, custom-error, circuit-breaker, api-key-validation, cache-response, validate-body, cors, set-vars, replace-body, compress-response, waf, bot-management. Note on phases: restrict-ips and rate-limit are valid in the on_tcp_connect/on_udp_connect phases; cors, set-vars, waf and bot-management must be in on_http_request; custom-error, replace-body and compress-response only in on_http_response; the rest in on_http_request.req.method, req.path, req.host, req.headers["x-…"], req.cookies["…"], req.query_params["…"], conn.client_ip, conn.ja3 (client TLS JA3 fingerprint), and vars.<name> (values set by earlier set-vars rules). Examples: req.method == "POST", req.path.startsWith("/api/"), vars.tier == "gold". Absent keys read "". Leave empty to always fire.rule_type — see rule-types catalog. Examples:
•
rate-limit: {"limit": int, "window": int (sec), "scope": "ip"|"endpoint"|"global"|"header"|"cookie"|"jwt", "key_name": string (required for header/cookie/jwt)}. The header/cookie/jwt scopes key on client-controlled values (JWT claim read without verifying the signature) — fair-sharing, not abuse protection; pair with an ip rule as the hard ceiling.
•
url-rewrite: {"pattern": regex, "replacement": string, "target": "path"|"query"|"host"|"path_query"} (target defaults to path).
•
cors: {"allow_origins": ["https://app.example.com"|"*"], "allow_methods": [...], "allow_headers": [...], "expose_headers": [...], "allow_credentials": bool, "max_age": int}. "*" may not be combined with allow_credentials.
•
set-vars: {"vars": {"tier": "req.headers[\"x-tier\"]", "is_admin": "req.path.startsWith(\"/admin\")"}} — each value is a CEL string expression; results are readable by later rules as vars.<name> and ${vars.<name>}.
•
replace-body (response phase): {"find": string, "replace": string, "regex": bool, "max_body": int (default 1 MiB)}. Applies to complete, uncompressed bodies only.
•
compress-response (response phase): {"min_size": int (default 1024), "content_types": ["text/","application/json"]}. Negotiates br (preferred) or gzip from Accept-Encoding.
•
waf (request phase): {"mode": "detect"|"block", "ruleset": "core"|"custom"|"core+custom", "custom_rules": string, "inspect_body": bool, "max_body_kb": int (default 128, max 1024)}. OWASP-style inspection via Coraza; mode defaults to detect (log only). core = built-in baseline (SQLi/XSS/traversal/scanners); custom supplies your own SecLang.
•
bot-management (request phase): {"mode": "detect"|"block", "ja3_denylist": ["<32-hex JA3>"], "ja3_allowlist": [...], "block_missing": bool, "message": string}. Matches the client's TLS JA3 fingerprint; mode defaults to detect. Denylist blocks listed fingerprints; a non-empty allowlist blocks everything NOT listed.
•
redirect: {"url": string, "status": 301|302|307|308, "pattern": string (optional regex)}; restrict-ips: {"allow": [cidr...], "deny": [cidr...]}.
Values in
add-headers, redirect and custom-response support ${cel-expression} interpolation (e.g. ${vars.tier}, ${conn.client_ip}), CRLF-sanitized.Response (201 Created)
The newly-created rule with assigned id and resolved config.
Errors
phase, invalid rule_type, missing name, schema-invalid config, or AI safety check flagged user-controlled content.rule_type.