Skip to main content

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.

Overrides 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.

Generated reference

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
}
FieldTypeRequiredNotes
policy_idstringyesPolicy 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_typestringyesstatic or dynamic.
override_reasonstringyes1-500 chars. Free text. Mandatory per ADR-044.
tool_signaturestringnoScope override to a specific tool name. Omit for tool-agnostic scope.
ttl_secondsintnoRequested 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.

MethodPathPurpose
POST/api/v1/system-policies/{id}/overrideCreate 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}/overrideFetch the override for a policy.
DELETE/api/v1/system-policies/{id}/overrideRemove the override.
GET/api/v1/system-policies/overridesList per-policy overrides.

In v11 a shipped policy can be re-actioned to one of four actions:

ActionMeaning
blockDeny the request. Most restrictive.
redactStrip matched content from the request or response and proceed.
warnAllow the request and emit a warning log.
logAllow 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​

StatusMeaning
400Invalid JSON body. Missing policy_id or override_reason, policy_type not static/dynamic, reason exceeds 500 chars. Or missing X-Tenant-ID header.
401Missing caller identity (X-User-Email / X-User-ID header)
403Policy is critical-risk (not overridable), or policy has allow_override=false
404Policy not found
500Policy 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​

NameTypeDescription
policy_idstringFilter to overrides for a specific policy. Accepts the UUID or the human-readable slug/name — the same identifier flexibility as create.
include_revokedboolInclude 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_by": "[email protected]",
"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​

StatusMeaning
400Missing X-Tenant-ID header
404Override not found within the caller's tenant
500Lookup 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​

StatusMeaning
400Missing X-Tenant-ID header (or blank override id)
401Missing caller identity (X-User-Email / X-User-ID header)
404Override not found within the caller's tenant, or already revoked
500Lookup 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:

EventWhen
override_createdSuccessful POST /api/v1/overrides
override_usedv10.x: policy evaluation consumed an active session override to flip a deny. In v11 a session override does not bypass the decision engine
override_expiredBackground cleanup detected TTL expiry
override_revokedSuccessful 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​

Operational Readiness Checklist​

Before relying on this page in a production rollout, pair it with the core operations docs: