System Policy API
The legacy system-policy API on the Agent, at /api/v1/system-policies, as it answers in v11.0.0.
In v11 the pattern-based system and tenant policy rows this API manages are read-only, and they decide nothing: one policy decision engine decides every request, from the shipped policy set and your organization's own typed policy documents. Every write on this API answers 409 LEGACY_POLICY_WRITE_FROZEN and names the route to use instead. The reads stay, so you can export what you had before you re-author it, and are removed in a later release. If you are upgrading, read the v10 → v11 Migration Guide.
/api/v1/static-policies is the deprecated spelling of the same routes, served by the same handlers with the same status codes and bodies; both spellings carry the headers described under Reading for export. Every path below uses the current spelling.
Request and response schemas live in the generated Agent API reference. Each endpoint below links to its operation there.
What replaces it
| You used this API to... | In v11 |
|---|---|
| see which built-in policies apply and what they do | Shipped Policy Posture: every shipped policy, its action and where it applies |
| turn a built-in policy off, or change its action, for your organization | per-policy control, which is part of v11 and does not go through this API; a detection-posture override re-actions the platform's policies in a whole category at once, not the organization-editable ones |
| add a pattern rule of your own | a typed policy document, through /api/v1/typed-policies or the portal's Policy Authoring page; see Typed Policy Authoring |
The typed policy routes are served by the Orchestrator and reachable through the Agent on every edition: GET /api/v1/typed-policies/edition (what this deployment may author), POST .../validate, POST .../publish, POST .../activate, GET .../active (the document in force) and GET .../system (the shipped system policy set, read-only). Community and Evaluation run the whole lifecycle on these routes. On Enterprise a publication must name approvers other than its author, and these routes name none, so an Enterprise publication through them is refused with APPROVER_IS_AUTHOR; an Enterprise organization publishes in the portal, which also serves compare, the published-version history and rollback.
Authentication
All endpoints require Authorization: Basic base64(clientId:clientSecret). The server derives tenant and organization context from the authenticated credentials; a supplied X-Tenant-ID header is ignored. On a Community deployment with no licence, requests without credentials are scoped to the default community tenant.
Errors use a common JSON shape. On the read routes code is the numeric HTTP status:
{
"error": {
"code": 404,
"message": "Policy not found"
}
}
The write refusal is the one exception: its code is the string LEGACY_POLICY_WRITE_FROZEN (see Writes answer 409).
Endpoints
| Method | Path | In v11 | Reference |
|---|---|---|---|
GET | /api/v1/system-policies | Read: list, for export | listSystemPolicies |
GET | /api/v1/system-policies/{id} | Read: one policy | getSystemPolicy |
GET | /api/v1/system-policies/{id}/versions | Read: version history | getSystemPolicyVersions |
GET | /api/v1/system-policies/effective | Read: the rows and overrides as v10.x combined them | getEffectiveSystemPolicies |
POST | /api/v1/system-policies/test | Removed in v11; see Testing a pattern | testSystemPolicyPattern |
POST | /api/v1/system-policies | Write: 409 | createSystemPolicy |
PUT | /api/v1/system-policies/{id} | Write: 409 | updateSystemPolicy |
DELETE | /api/v1/system-policies/{id} | Write: 409 | deleteSystemPolicy |
PATCH | /api/v1/system-policies/{id} | Write: 409 | toggleSystemPolicy |
POST, GET, DELETE | /api/v1/system-policies/{id}/override | Legacy: the v11 engine does not read it; see Policy overrides | createSystemPolicyOverride |
GET | /api/v1/system-policies/overrides | Legacy: lists the overrides the v11 engine does not read | listSystemPolicyOverrides |
Writes answer 409
Create, update, delete and the enabled toggle all write the legacy policy table, which v11 makes read-only to the application database role. Each answers 409 Conflict:
{
"error": {
"code": "LEGACY_POLICY_WRITE_FROZEN",
"message": "The legacy policy tables are read-only in v11: migrations/core/172 revoked write access from the application role, and this endpoint writes them. Author policies through the typed authoring route at /api/v1/typed-policies instead. Reads on this endpoint are unaffected."
}
}
No licence, edition or upgrade changes this answer: the legacy write path is retired for every caller. Branch on error.code; the message names the replacement route.
A deployment that connects to its database as the owner rather than as the application role (AXONFLOW_DB_USE_APP_ROLE=false) is not bound by the revoke, so the write is not refused there. Nothing the v11 engine decides reads the row it writes.
Reading for export
These read routes are a deprecated export surface in v11. Every response on these routes (reads, the 409 on writes, 401 and 503) carries Link: </api/v1/typed-policies>; rel="successor-version" and X-AxonFlow-Removed-In: v11.1, and from the v11.0.0 release a Deprecation header (RFC 9745) dated to the release; v11.1 removes the routes. A path or method these routes do not serve (404, 405) carries none of them.
The read routes answer as they did in v10.x, so an integration can export the rows it had before re-authoring them as typed policy.
GET /api/v1/system-policies
curl "http://localhost:8080/api/v1/system-policies?tier=tenant&limit=100" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
| Parameter | Type | Default | Description |
|---|---|---|---|
enabled | boolean | (none — returns all) | Filter by enabled status |
category | string | (none — returns all) | Filter by category: security-sqli, security-admin, security-dangerous, pii-global, pii-us, pii-eu, pii-india, pii-singapore, pii-indonesia, code-secrets, code-unsafe, code-compliance, sensitive-data |
tier | string | (none — returns all) | Filter by tier: system, organization, or tenant. tenant and organization are the rows your organization authored |
search | string | (none — returns all) | Case-insensitive match on name or description |
page | integer | 1 | Page number |
limit | integer | 20 (max: 100) | Preferred items-per-page parameter |
page_size | integer | Deprecated | Backward-compatible alias for limit (only read when limit is absent) |
Response (200 OK):
{
"policies": [
{
"id": "01JTENANTPOLICY1234567890",
"policy_id": "custom_ab12cd34ef56",
"name": "Customer Account Pattern",
"description": "Detects internal customer account identifiers",
"category": "pii-global",
"tier": "tenant",
"pattern": "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b",
"action": "log",
"severity": "medium",
"enabled": true,
"priority": 50,
"tenant_id": "my-tenant",
"version": 3,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T12:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 24,
"total_pages": 1
}
}
Policy objects also carry risk_level and allow_override, plus optional org_id, organization_id, tags, metadata, created_by, updated_by and deleted_at fields when populated. The action on a row is what v10.x stored for it; what v11 does with the corresponding shipped policy is in Shipped Policy Posture.
GET /api/v1/system-policies/{id}
Returns one policy object, the same shape as a list entry. {id} is the row UUID or the policy_id slug.
GET /api/v1/system-policies/{id}/versions
{
"policy_id": "custom_ab12cd34ef56",
"versions": [
{
"id": "8f14e45f-ceea-467f-a9d4-91b2c3d4e5f6",
"policy_id": "custom_ab12cd34ef56",
"version": 4,
"snapshot": {
"name": "Block Competitor Mentions",
"pattern": "(?i)(competitor-a|competitor-b|competitor-c|rival-product)",
"action": "block",
"enabled": true
},
"change_type": "update",
"change_summary": "Policy updated",
"changed_at": "2025-01-02T14:00:00Z"
}
],
"count": 1
}
Each entry carries the complete policy state at that version in snapshot (abridged above), plus change_type (create, update, enable, disable, delete), change_summary, changed_by and changed_at. Community returns the last 5 versions; Enterprise returns the full history.
GET /api/v1/system-policies/effective
Returns the rows with their per-policy overrides applied, the way v10.x combined them, under static (the dynamic array is always empty on this endpoint). It describes the legacy rows, not what the v11 engine enforces.
Testing a pattern
POST /api/v1/system-policies/test is removed in v11: it evaluated a pattern the way the legacy engine did, and v11 has no legacy engine to answer for. To check what a policy decides, use policy simulation and policy testing, which answer with the engine that enforces.
Policy overrides
In v11 these routes are legacy, deprecated with the rest of this API. The v11 engine does not read the table these routes record into, so an override recorded here decides nothing. Overrides recorded before the upgrade are imported once into v11's per-policy control, and the table then becomes read-only. Per-policy control of each shipped policy for your organization is part of v11 and does not go through these routes. In v11 a shipped policy can be re-actioned to block, redact, warn or log.
The route's request fields are action_override, enabled_override, override_reason (required) and expires_at (optional). GET on the same path returns an override, DELETE removes it, and GET /api/v1/system-policies/overrides (alias GET /api/v1/policy-overrides) lists your organization's overrides, with include_expired=true to include lapsed ones; the Overrides API page covers the record's full shape. To change what the v11 engine does for a whole category, record a detection-posture override: it re-actions the platform's policies in that category (pii, sqli or dangerous_command) on the planes that apply organization overrides, and not the 22 organization-editable policies.
Error responses
| HTTP Status | When |
|---|---|
| 400 | Malformed JSON body, missing override_reason on an override |
| 401 | Tenant could not be determined from the request credentials |
| 403 | On v10.x, an override without an Enterprise licence |
| 404 | Policy or override does not exist |
| 409 | LEGACY_POLICY_WRITE_FROZEN on any write to the policy rows; an override that already exists |
| 500 | Internal error |
SDK example: export your rows (Go)
policies, _ := client.ListStaticPolicies(&axonflow.ListStaticPoliciesOptions{
Tier: axonflow.TierTenant,
})
for _, p := range policies {
fmt.Printf("%s\t%s\t%s\t%s\n", p.ID, p.Name, p.Pattern, p.Action)
}
Related
- Shipped Policy Posture
- Typed Policy Authoring
- Policy and Identity Control Plane
- Tenant Policy API: the legacy condition-based family, read-only in v11 too
- Security - PII Detection and Security - SQL Injection: the detectors behind the shipped policies
