Skip to main content

Session Overrides

Available on platform v7.1.0+. Requires an Evaluation or Enterprise license.

Session overrides give a user a governed, time-bounded, audit-logged way to unblock themselves from a policy deny. Instead of silently disabling the plugin or editing code out-of-band, the user creates a scoped override that flips the next decision from deny to allow for a short window.

Every override is recorded in the audit log with the user's justification, the policy it overrides, the tool it's scoped to (if any), and its expiry. The full lifecycle — override_created, override_used, override_expired, override_revoked — is reconstructable from audit search alone.

Role-scoped reads (platform v9.10.0+, Enterprise)

On fleet deployments, listing and revoking overrides is scoped by the caller's validated per-user token: an admin-role token sees the tenant's overrides, a developer-role token sees (and can revoke) only its own, and a caller with no per-user identity sees zero rows (a behavior change — previously the shared tenant credential listed everything).

When to use them

  • A policy deny is a false positive in a specific debugging context.
  • A developer needs short-term access to unblock a legitimate workflow.
  • An admin is investigating a policy's blast radius before broader tuning.

Overrides are not for structural policy changes. If a policy's rule should be softened permanently, edit the policy — don't pile overrides on top.

Before reaching for an override, it's worth understanding why the policy fired: which detector tier matched, and whether the governed tool should have been evaluated by that detector at all. See How AxonFlow Detection Works.

Rules (ADR-042)

RuleValue
Default TTL60 minutes
Hard cap24 hours
Minimum TTL1 minute
Critical-risk policiesCannot be overridden. Server rejects with 403.
allow_override=false on any policyCannot be overridden. Server rejects with 403.
JustificationMandatory free-text field (1-500 chars)
TTL enforcementServer-side — plugin may suggest shorter; platform clamps to the hard cap.

The critical-risk invariant is enforced at the database level via a trigger, not by convention. If a policy writer sets risk_level=critical, the trigger forces allow_override=false regardless of what the policy document says.

Creating an override

curl -X POST https://your-platform/api/v1/overrides \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"override_reason": "Debugging prod incident INC-4521 — temporarily allowing test SQL",
"tool_signature": "Bash",
"ttl_seconds": 900
}'

Response:

{
"id": "ov-f3a81c...",
"policy_id": "pol-sqli-detector",
"policy_type": "static",
"expires_at": "2026-04-17T13:15:00Z",
"ttl_seconds": 900,
"requested_ttl": 900,
"clamped": false,
"created_at": "2026-04-17T13:00:00Z"
}

If the TTL was clamped:

{
"id": "ov-...",
"ttl_seconds": 86400,
"requested_ttl": 172800,
"clamped": true,
"clamped_reason": "exceeds_hard_cap"
}

SDK usage

Session overrides (the time-bounded, tool-scoped, audit-logged /api/v1/overrides endpoint described on this page) do not yet have a dedicated SDK wrapper. Call the endpoint with the raw HTTP contract shown above, or through a plugin client that wraps it (for example the OpenClaw plugin's TypeScript client). The TTL clamp (60-minute default, 24-hour hard cap) and the critical-risk invariant are enforced server-side, so a caller cannot exceed the cap or override a critical-risk policy regardless of the arguments it sends.

Not the same as the SDK createPolicyOverride methods

The TypeScript, Python, Go, and Java SDKs expose createPolicyOverride, deletePolicyOverride, and listPolicyOverrides (the Rust SDK does not). Those manage a different mechanism: a persistent action-override on a specific system (built-in, static) policy via POST /api/v1/static-policies/{id}/override (fields action_override, override_reason, optional expires_at). Action overrides apply to system policies only — a system policy is immutable, so an override is how you adjust its effective action. Your own organization and tenant policies are not overridden this way; you edit or delete them directly, since they are the single source of truth for their own rule. The override action is one of block, require_approval, redact, warn, or log — notably not allow (allow is the implicit default, never an override target), so an override can only tighten or reshape a system policy's action, never silently open it up. These are not the time-bounded session overrides on this page and do not carry tool_signature, ttl_seconds, or the session TTL-clamp semantics.

MCP tool surface

The full override lifecycle is also exposed as MCP tools by the agent's MCP server at /api/v1/mcp-server. Every plugin (OpenClaw, Claude Code, Cursor, Codex) points its MCP client at the same endpoint, so these tools are available wherever an agent runs — no per-plugin tool registration is required.

MCP toolHTTP endpointArguments
create_overridePOST /api/v1/overridespolicy_id, policy_type, override_reason, optional tool_signature, ttl_seconds
delete_overrideDELETE /api/v1/overrides/{id}override_id
list_overridesGET /api/v1/overridesoptional policy_id, include_revoked

The same MCP server also serves the explain_decision tool, documented on the Explainability page.

Verify registration against a live stack:

curl -s -X POST https://your-platform/api/v1/mcp-server \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/list"}' \
| jq '.result.tools[] | .name'

These tools require an authenticated user identity; the agent threads X-User-ID / X-User-Email through from the MCP session, so plugin configuration never needs to expose it.

Scoping

An override applies to a tuple of (policy_id, user, tenant, optional tool_signature).

  • Tool-agnostic (tool_signature omitted) — applies to every matching tool call from this user.
  • Tool-scoped (tool_signature set) — applies only when that specific tool is invoked.

A tool-scoped override beats a tool-agnostic one when both exist for the same policy and user. Inside the same scope, first-match-wins by created_at DESC.

Revoking

curl -X DELETE https://your-platform/api/v1/overrides/{override_id} \
-u "$CLIENT_ID:$CLIENT_SECRET"

Revocation is immediate. The next policy evaluation after revoked_at does not consult the override.

Revocations are triggered by:

  • The user who created it (DELETE /api/v1/overrides/:id)
  • An org admin (same endpoint, different auth)
  • A policy change — if allow_override flips to false, all active overrides for that policy are revoked with reason policy_changed.

Auditing

Four new audit event types:

EventEmitted when
override_createdOverride record successfully created
override_usedPolicy eval applied an active override to flip a deny
override_expiredBackground cleanup detected TTL expiry
override_revokedUser or admin explicitly revoked

Search for everything tied to one override:

curl -X POST https://your-platform/api/v1/audit/search \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"override_id": "ov-f3a81c..."}'

Tier split

TierWhat's available
CommunitySee override context in approval prompts + audit records. Cannot create overrides.
EvaluationCreate session-scoped overrides up to 60m default / 24h cap.
EnterpriseAbove + org-wide override listings, admin revocation at scale, extended audit retention, cross-plugin override history.

See also

  • Explainability — get the full decision context that precedes an override decision.
  • Audit Logging — find overrides in the audit trail.
  • Human in the Loop — for decisions that need a second-person approval rather than a session bypass.

Rollout Checklist

Use this page as one layer of the broader governance rollout: