Exception requests
Field-level reference for exception requests, their statuses, who can act on them, and the notifications they send.
An exception request proposes an exception for a firewall, together with a justification. Anyone with fw:access can make one on a firewall that accepts requests. A reviewer approves it, which adds the exception to the firewall, or denies it with a comment. For the workflow, see Set up an exception workflow.
Shape
{
"id": "exr-01JZ8Q2V6R9X3T5W7Y0A2C4E6G",
"firewallId": "3e7f5cb806f26e3beae2",
"status": "approved",
"proposedException": {
"id": "allow-lodash-41720",
"rule": "block-critical-vulns",
"selector": { "packageName": "lodash", "version": "4.17.20" },
"log": true,
"expires": "2026-11-15T00:00:00Z",
"reason": "not_reachable"
},
"justification": "Only the template helper is used. The vulnerable merge function is never called.",
"requestedBy": "user_2a9f",
"requestedAt": "2026-10-15T09:12:00Z",
"decidedBy": "user_7c1d",
"decidedAt": "2026-10-15T10:40:00Z",
"decisionComment": "Approved for one month while the upgrade is planned.",
"exceptionId": "allow-lodash-41720",
"updatedAt": "2026-10-15T10:40:00Z"
}Fields
| Field | Type | Description |
|---|---|---|
id | string | Generated when the request is made |
firewallId | string | The firewall the exception would be added to |
status | string | pending, approved, denied, or withdrawn. See Statuses |
proposedException | object | The exception as requested, in the exception shape. Its rule must be a rule on the firewall, its own or inherited |
justification | string | Why the requester needs the exception. Required |
requestedBy, requestedAt | string | The requester's user ID, and when the request was made |
decidedBy, decidedAt | string | When set: who approved, denied, or withdrew the request, and when |
decisionComment | string | When set: the comment sent to the requester. Always set on a denied request |
exceptionId | string | When set, on an approved request: the ID of the exception the approval added |
updatedAt | string | Last change to the request |
The reviewer can edit the exception before approving, so the exception added can differ from proposedException. exceptionId points to the exception as added.
Statuses
| Status | Meaning |
|---|---|
pending | Waiting for a decision |
approved | The exception was added. Final |
denied | Turned down with a comment. Can still be approved later |
withdrawn | Closed without a decision, by the requester or a reviewer. Final |
approved is the decision. If the exception later expires, is deleted, or is removed by a rollback, the request stays approved.
Permissions
| Action | Needs |
|---|---|
| Request | fw:access on the firewall, and a person: a dashboard session or a personal access token. Service access tokens cannot request |
| Read requests | fw:read on the firewall |
| Approve | fw:update:exception on the firewall. For an inherited rule, also on the firewall that owns the rule |
| Deny | fw:update:exception on the firewall |
| Withdraw | The requester, or anyone with fw:update:exception on the firewall |
Being on the notify team only means receiving email. Needs review in the dashboard lists pending requests on every firewall where you have fw:update:exception, whichever team gives it to you. See Permissions.
Firewall setting
Exception requests are configured per firewall in the exceptionRequests field of the firewall config:
{
"exceptionRequests": {
"enabled": true,
"teamId": "platform"
}
}| Field | Type | Description |
|---|---|---|
enabled | boolean | Whether the firewall accepts requests. Off when the field is absent |
teamId | string | The notify team, emailed on every new request. Required, and must name an existing team, when enabled is true |
- Child firewalls do not inherit the setting.
- A firewall cloned from a template takes the template's setting, unless the create request sets its own.
- The setting is part of the config version, so a rollback restores the setting of that version.
- Deleting a firewall deletes its requests.
When requests are turned off
Existing requests stay readable, and pending ones stay pending. No one can make, approve, deny, or withdraw a request until requests are turned on again.
Identical requests
Approving a request also approves the other pending requests on the firewall that ask for the same thing, and links them to the same exception. A request counts as the same when its rule and selector match the original proposal or the exception as approved. Each requester gets an email.
Notifications
| When | Who gets it |
|---|---|
| A request is made | Members of the notify team, except the requester |
| A request is approved, denied, or withdrawn | The requester, unless they made the change themselves |
Emails name the firewall, the rule by its description and ID (for example Block critical vulnerabilities (block-critical-vulns)), the package, version, and expiry. A decision email includes the reviewer's comment. Each email links to the request in the dashboard.
Webhook events
exception-request.created, exception-request.approved, exception-request.denied, and exception-request.withdrawn carry the full request after the change. See Webhooks.
Audit log
exception_request.create, exception_request.approve, exception_request.deny, and exception_request.withdraw record each action. An approval also records firewall.exception.upsert for the added exception, with exception_request_id in its event data. See Audit log.
Related
- How-to: Set up an exception workflow, Manage exceptions
- Concept: Exception model
- Reference: Exceptions, Permissions, Webhooks
- API: Rules and exceptions