Overrides API
Endpoints for the session-scoped policy overrides of v10.x, which in v11 no longer bypass the policy engine, and the legacy per-policy override routes, which the v11 engine does not read. The session overrides implement the platform's override-semantics architecture decision (ADR-044).
Available on platform v7.1.0+. These endpoints are served by the Orchestrator and proxied through the Agent gateway on 8080; the gateway authenticates the caller (Basic auth) and sets the trusted X-Tenant-ID / X-Org-ID / X-User-Email identity headers the handlers require. Callers hitting the Orchestrator directly must set those headers themselves.
In v10.x an override created through this API was a session "allow" override: for its TTL it bypassed the matched policy's deny for the creating user's tenant scope. In v11 it does not bypass the engine. Replacing a policy's action (e.g. flipping block to warn) was a different, per-policy operation in v10.x; Per-policy action overrides below says what its routes do in v11.
The two kinds of override on this page are different things in v11:
- The per-policy action and enabled override (
/api/v1/system-policies/{id}/override) is a legacy route in v11, deprecated with the rest of the system-policy API. The v11 engine does not read what it records, so an override there decides nothing; existing overrides are imported once at upgrade into v11's per-policy control, and their table then becomes read-only. Changing one shipped policy for your organization is part of v11 and does not go through this route. - The session allow-bypass override (
/api/v1/overrides, the rest of this page) is not a lever in v11. There is no way to switch the engine off, and a session bypass does not silence it. A time-boxed, approved exception is expressed as a typed policy exception with its own audit row.
The override that reaches the v11 engine is per category: a detection-posture override re-actions the platform's policies in its category (pii, sqli or dangerous_command) on the planes that apply organization overrides. It does not reach the 22 organization-editable policies, which your organization changes in its own typed policy document.
The session-scoped override endpoints on this page are in the
Orchestrator API reference
(createPolicyOverride,
listPolicyOverrides,
getPolicyOverride,
revokePolicyOverride).
The legacy per-policy override routes, including the /api/v1/policy-overrides
alias for their list, are in the
Agent API reference
(listPolicyOverrides,
listSystemPolicyOverrides,
createSystemPolicyOverride).
POST /api/v1/overrides
Create a session-scoped override.
Request body
{
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"override_reason": "Debugging prod incident INC-4521",
"tool_signature": "Bash",
"ttl_seconds": 900
}
| Field | Type | Required | Notes |
|---|---|---|---|
policy_id | string | yes | Policy to override. Accepts either the UUID or the human-readable identifier (the policy_id slug for static policies, the name for dynamic policies), so you can pass the policy_id from a block response's policy_matches[] straight through. |
policy_type | string | yes | static or dynamic. |
override_reason | string | yes | 1-500 chars. Free text. Mandatory per ADR-044. |
tool_signature | string | no | Scope override to a specific tool name. Omit for tool-agnostic scope. |
ttl_seconds | int | no | Requested TTL. Clamped server-side. Default 3600 (60m), hard cap 86400 (24h), min 60 (1m). |
Required identity headers (set automatically when the request comes through the Agent gateway): X-User-Email (or X-User-ID) — missing returns 401; X-Tenant-ID — missing returns 400. X-Org-ID is used as the row's organization scope when present.
The created override always records the session allow action — this endpoint does not accept an action_override or enabled_override field. Those belong to the per-policy override surface below.
Per-policy action overrides
Replacing a system policy's terminal action (or disabling it) for a whole tenant was a policy-management operation on the Agent's system-policy API, not this endpoint. In v11 these routes are legacy, deprecated with that API, and the v11 engine does not read what they recorded, so an override there decides nothing. Overrides recorded before the upgrade are imported once into v11's per-policy control, and their table then becomes read-only.
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/system-policies/{id}/override | Create an action/enabled override for a system policy (action_override, enabled_override, override_reason, optional expires_at). On v10.x this required an Enterprise licence (403 otherwise). A duplicate override returns 409. |
GET | /api/v1/system-policies/{id}/override | Fetch the override for a policy. |
DELETE | /api/v1/system-policies/{id}/override | Remove the override. |
GET | /api/v1/system-policies/overrides | List per-policy overrides. |
In v11 a shipped policy can be re-actioned to one of four actions:
| Action | Meaning |
|---|---|
block | Deny the request. Most restrictive. |
redact | Strip matched content from the request or response and proceed. |
warn | Allow the request and emit a warning log. |
log | Allow the request and record an audit entry. Least restrictive. |
require_approval, valid as an override target in v10.x, is not a v11 re-action: a shipped policy has no approver pool for an approval to bind to. Authoring-only actions (alert, route, modify_risk) were valid for defining a legacy tenant policy, which v11 no longer accepts writes for, and are rejected as override targets (invalid override action).
Response (201)
{
"id": "ov-f3a81c-...",
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"expires_at": "2026-04-17T13:15:00Z",
"ttl_seconds": 900,
"requested_ttl": 900,
"created_at": "2026-04-17T13:00:00Z"
}
requested_ttl, clamped, and clamped_reason are omitempty — they appear only when a TTL was explicitly requested / actually clamped. When TTL is clamped:
{
"id": "ov-...",
"ttl_seconds": 86400,
"requested_ttl": 172800,
"clamped": true,
"clamped_reason": "exceeds_hard_cap"
}
clamped_reason is exceeds_hard_cap (requested above 86400s) or below_minimum (requested below 60s).
Error responses
| Status | Meaning |
|---|---|
| 400 | Invalid JSON body. Missing policy_id or override_reason, policy_type not static/dynamic, reason exceeds 500 chars. Or missing X-Tenant-ID header. |
| 401 | Missing caller identity (X-User-Email / X-User-ID header) |
| 403 | Policy is critical-risk (not overridable), or policy has allow_override=false |
| 404 | Policy not found |
| 500 | Policy lookup or insert failure |
Critical-risk rejection is enforced at the database level via trigger. allow_override=true on a critical-risk policy is coerced to false at insert time, so the server will always return 403 regardless of any race.
GET /api/v1/overrides
List active overrides scoped to the caller's tenant.
Query parameters
| Name | Type | Description |
|---|---|---|
policy_id | string | Filter to overrides for a specific policy. Accepts the UUID or the human-readable slug/name — the same identifier flexibility as create. |
include_revoked | bool | Include revoked overrides in results. Default false. |
Returns at most 100 overrides, newest first.
Response (200)
{
"overrides": [
{
"id": "ov-...",
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"tenant_id": "tenant-x",
"override_reason": "Debugging",
"expires_at": "2026-04-17T13:15:00Z",
"created_at": "2026-04-17T13:00:00Z"
}
],
"count": 1
}
Requires the X-Tenant-ID header (set automatically by the agent); a missing header returns 400.
GET /api/v1/overrides/{id}
Fetch a single override by ID.
Response (200)
{
"id": "ov-...",
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"tenant_id": "tenant-x",
"organization_id": "org-y",
"tool_signature": "Bash",
"override_reason": "Debugging",
"expires_at": "2026-04-17T13:15:00Z",
"created_at": "2026-04-17T13:00:00Z"
}
Null-valued optional fields (tool_signature, revoked_at, revoked_by, etc.) are omitted from the actual response rather than serialized as null. The lookup is scoped to the caller's tenant, so an override belonging to another tenant returns 404.
Error responses
| Status | Meaning |
|---|---|
| 400 | Missing X-Tenant-ID header |
| 404 | Override not found within the caller's tenant |
| 500 | Lookup failure |
DELETE /api/v1/overrides/{id}
Revoke an override. Next policy evaluation after the revocation does not consult it.
Response (200)
{
"id": "ov-...",
"revoked_at": "2026-04-17T13:10:00Z"
}
Error responses
| Status | Meaning |
|---|---|
| 400 | Missing X-Tenant-ID header (or blank override id) |
| 401 | Missing caller identity (X-User-Email / X-User-ID header) |
| 404 | Override not found within the caller's tenant, or already revoked |
| 500 | Lookup or update failure |
The lookup and the update are both tenant-scoped, so one tenant cannot revoke another tenant's override even with a known UUID. Emits an override_revoked audit event.
Audit events
Every override lifecycle event writes an entry to the audit log with its own request_type:
| Event | When |
|---|---|
override_created | Successful POST /api/v1/overrides |
override_used | v10.x: policy evaluation consumed an active session override to flip a deny. In v11 a session override does not bypass the decision engine |
override_expired | Background cleanup detected TTL expiry |
override_revoked | Successful DELETE (user, admin, or policy-change-triggered) |
Lifecycle rows carry policy_decision: "override_lifecycle" — a recognized non-verdict marker — so they never appear in the verdict-centric decision feed (GET /api/v1/decisions) or inflate the per-action counts in POST /api/v1/audit/report.
Audit record policy_details includes: override_id, policy_ids, reason, ttl_seconds, requested_ttl, clamped, plus tool_signature when the override is tool-scoped, decision_id for override_used, and revoked_by for override_revoked. Search for all events tied to one override:
curl -X POST /api/v1/audit/search \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"override_id": "ov-f3a81c-..."}'
Listing per-policy overrides through the canonical alias
Starting on platform v7.2.0, the agent exposes a canonical /api/v1/policy-overrides GET alias for the tenant's per-policy override list (the same list as GET /api/v1/system-policies/overrides, with an optional include_expired=true query param). It matches the existing policy-categories / static-policies / dynamic-policies naming pattern. In v11 it is a legacy route, deprecated with the system-policy API, and the v11 engine does not read the list it returns.
curl http://localhost:8080/api/v1/policy-overrides \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
The response envelope is {"overrides": [...], "count": N}, where each row is a per-policy override record (action_override / enabled_override and scope fields) — not the session-override summary returned by GET /api/v1/overrides above. The alias exists because the Customer Portal's overrides handler proxies to the canonical path; callers using the canonical path against a pre-v7.2.0 agent get 404 page not found. Pre-v7.2.0 deployments can use /api/v1/static-policies/overrides for the same list.
See also
- Session Overrides concept
- Audit API with filters
decision_id,policy_name,override_id - Explainability to figure out whether an override is available before creating one
- HITL Approval Gates for the approval queue; in v11 an override does not route a request there
Operational Readiness Checklist
Before relying on this page in a production rollout, pair it with the core operations docs:
- Deployment Mode Matrix for self-hosted, Evaluation, Enterprise, SaaS, and In-VPC fit
- Failure Modes And Recovery for degraded-provider, connector, approval, and runtime behavior
- Capacity Planning for sizing and growth signals
- Community vs Evaluation vs Enterprise for limits, support surfaces, and upgrade triggers
