Unified Policy API
The Unified Policy API gives the Customer Portal one source-agnostic surface to read and write policies across both planes AxonFlow governs:
- static policies — pattern/regex based, evaluated by the Agent (see the System Policy API)
- dynamic policies — condition based, evaluated by the Orchestrator (see the Tenant Policy API)
Instead of the caller choosing an endpoint per policy plane, the unified API resolves each policy's source automatically and dispatches to the correct underlying service. Auth, tenant/organization scoping, and behavior are identical to calling the per-plane endpoints directly — the unified API is a thin dispatcher, not a new policy engine or a merged schema.
API Endpoint: /api/v1/unified-policies (Customer Portal)
The Customer Portal and its unified policy surface are an Enterprise feature. The underlying per-plane APIs are available in all editions.
Why a unified surface
Historically the portal UI had to know, per policy, whether it was static (Agent) or dynamic (Orchestrator) and call a different write endpoint accordingly. That entrenched the split in every client. The unified API removes that: read and write flow through one path, and the source is resolved server-side using the same lookup for reads and writes, so a policy is never classified two different ways.
The unified surface deliberately does not merge the two policy schemas — a
static policy still has a pattern/severity, a dynamic policy still has
conditions/actions. Merging the write schema and renaming the tier
terminology are tracked separately and are not part of this API.
Read endpoints
| Method & Path | Purpose |
|---|---|
GET /api/v1/unified-policies | List policies from both sources, source-tagged, paginated |
GET /api/v1/unified-policies/summary | Counts by source, tier, category, severity |
GET /api/v1/unified-policies/effective | Effective policies with overrides applied |
GET /api/v1/unified-policies/{id} | A single policy from whichever source owns it |
Every policy in a response carries a source field ("static" or "dynamic").
List/summary/effective responses also carry partial: true and source_errors
when one source could not be reached, so an unavailable plane is surfaced rather
than silently dropped.
Write endpoints
| Method & Path | Source resolution | Dispatches to |
|---|---|---|
POST /api/v1/unified-policies | source field in the body (required) | static → Agent create; dynamic → Orchestrator create |
PUT /api/v1/unified-policies/{id} | resolved from the id | Agent / Orchestrator update |
DELETE /api/v1/unified-policies/{id} | resolved from the id | Agent / Orchestrator delete |
POST /api/v1/unified-policies/{id}/toggle | resolved from the id | enable/disable on the owning plane |
POST|GET|DELETE /api/v1/unified-policies/{id}/override | resolved from the id | Agent static override only |
Source resolution
For id-addressed verbs (update / delete / toggle / override) the source is resolved by looking the id up on both planes (tenant-scoped, static preferred if both match) — the same lookup the read endpoints use. Resolution is fail-closed:
- found on exactly one plane → dispatched there
- absent from both planes →
404, no write - a plane could not be reached, so ownership is unknown →
502, no write (the dispatcher never guesses a plane)
For POST (create) there is no id to resolve, so the body must include a
source of "static" or "dynamic". A missing or unknown source is rejected
with 400.
Create
# Create a dynamic (condition-based) tenant policy
curl -X POST https://portal.example.com/api/v1/unified-policies \
-H 'Content-Type: application/json' --cookie "$SESSION" \
-d '{
"source": "dynamic",
"name": "Block risky queries",
"type": "risk",
"tier": "tenant",
"conditions": [{"field": "risk_score", "operator": "greater_than", "value": 0.8}],
"actions": [{"type": "block"}],
"enabled": true
}'
# Create a static (pattern-based) tenant policy
curl -X POST https://portal.example.com/api/v1/unified-policies \
-H 'Content-Type: application/json' --cookie "$SESSION" \
-d '{
"source": "static",
"name": "Internal ticket IDs",
"category": "pii-global",
"tier": "tenant",
"pattern": "TICKET-[0-9]+",
"action": "redact",
"severity": "low",
"enabled": true
}'
Every field other than source is passed through unchanged to the underlying
create endpoint, which owns the real schema (see the
System Policy API and
Tenant Policy API for the full field sets).
Update, delete, toggle
# Update — source resolved from the id
curl -X PUT .../api/v1/unified-policies/{id} -d '{"description": "…"}'
# Toggle enable/disable — body carries the target state
curl -X POST .../api/v1/unified-policies/{id}/toggle -d '{"enabled": false}'
# Delete
curl -X DELETE .../api/v1/unified-policies/{id}
Override (static-only)
Overrides apply to system/static policies only: a system policy's
pattern is immutable, so an override adjusts its action or enabled state
without changing the pattern. Dynamic/tenant policies are changed directly with
update/toggle instead — an override request against a dynamic policy is rejected
with 400.
# Override a system static policy's action (Enterprise)
curl -X POST .../api/v1/unified-policies/{system-policy-id}/override \
-H 'Content-Type: application/json' --cookie "$SESSION" \
-d '{"action_override": "warn", "override_reason": "Monitoring false positives"}'
# Read / revoke the override
curl -X GET .../api/v1/unified-policies/{system-policy-id}/override
curl -X DELETE .../api/v1/unified-policies/{system-policy-id}/override
See Overrides API and Governance → Overrides for override semantics, expiry, and the least-permissive rules.
Relationship to the per-plane APIs
The unified API dispatches to — it does not replace — the per-plane endpoints:
| Plane | Direct API | Service |
|---|---|---|
| static | /api/v1/static-policies | Agent |
| dynamic | /api/v1/policies | Orchestrator |
The direct endpoints remain available for backward compatibility. New
portal-facing write integrations should target /api/v1/unified-policies/*.
The direct write routes are candidates for deprecation once clients migrate and
the system/tenant API terminology alignment lands.
Related
- System Policy API — static/pattern plane
- Tenant Policy API — dynamic/condition plane
- Overrides API — override lifecycle
- Governance → Overrides — override semantics
