System Policy API
Manage pattern-based system and tenant policies through the Agent API. This API covers both:
- the built-in system policy baseline that AxonFlow ships and maintains
- tenant or organization policies you add for custom pattern-based governance
API Endpoint: /api/v1/static-policies
Overview
System and tenant pattern-based policies (accessed via /api/v1/static-policies) are evaluated synchronously on every request. They:
- Detect SQL injection attempts
- Identify PII (credit cards, SSNs, emails, phone numbers)
- Block or warn on custom patterns
- Support tenant-specific overrides (Enterprise)
Base URL: http://localhost:8080 (Agent)
Authentication
All static policy endpoints require Authorization: Basic base64(clientId:clientSecret). The server derives tenant and org context from the authenticated credentials.
- Tenant context is extracted from the authenticated
clientId— no separate header needed. A suppliedX-Tenant-IDheader is ignored (deprecated alias; the auth-derived tenant always wins). - Community mode (self-hosted, no license): requests without credentials are accepted and scoped to the default community tenant.
- Organization-tier policies use
organization_idin the request body (not a header) X-User-IDis used on mutating operations for audit attribution
Errors use a common JSON shape, where code is the numeric HTTP status:
{
"error": {
"code": 403,
"message": "System policies cannot be modified"
}
}
Endpoints
GET /api/v1/static-policies
List all system and tenant pattern-based policies with optional filtering.
Request:
curl "http://localhost:8080/api/v1/static-policies?enabled=true&category=security-sqli&tier=system&limit=20" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Query Parameters:
| 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 |
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": "01JSTATICPOLICY1234567890",
"policy_id": "sys_sqli_union_select",
"name": "UNION SELECT Detection",
"description": "Detects UNION-based SQL injection attempts",
"category": "security-sqli",
"tier": "system",
"pattern": "(?i)union\\s+(all\\s+)?select",
"action": "block",
"severity": "critical",
"priority": 100,
"enabled": true,
"tenant_id": "",
"version": 1,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
},
{
"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 (string) and allow_override (boolean) fields used by the session-override enforcement path, plus optional org_id, organization_id, tags, metadata, created_by, updated_by, and deleted_at fields when populated.
POST /api/v1/static-policies
Create a new static policy.
Request:
curl -X POST http://localhost:8080/api/v1/static-policies \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-H "X-User-ID: [email protected]" \
-d '{
"name": "Block Competitor Mentions",
"description": "Block queries mentioning competitor products",
"category": "security-admin",
"tier": "tenant",
"pattern": "(?i)(competitor-a|competitor-b|rival-product)",
"action": "block",
"severity": "medium",
"priority": 90,
"enabled": true,
"tags": ["competitive-intelligence", "sales"]
}'
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | — | Policy display name |
description | string | No | — | Policy description |
category | string | Yes | — | One of the valid static categories: 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. Any other value is rejected with 400 |
tier | string | No | tenant | Tier: tenant or organization via API (system is rejected with 403) |
organization_id | string | No | — | Organization identifier; required when tier is organization |
pattern | string | Yes | — | RE2 regex pattern to match (validated for syntax, length, and capture-group limits; invalid patterns return 400) |
action | string | Yes | — | Action: block, require_approval, redact, warn, or log |
severity | string | No | — | Severity: critical, high, medium, low. No server-side default is applied on create — set it explicitly |
priority | integer | No | 50 | Evaluation priority (higher runs earlier). Omitted or 0 is replaced with the default 50 |
enabled | boolean | No | false | Whether policy is active |
tags | array | No | — | Tags for filtering and internal organization |
The generated policy_id uses a custom_ prefix (for example custom_ab12cd34ef56); system-seeded policies use sys_* or legacy snake-case identifiers. Per-policy endpoints accept either the row UUID (id) or the policy_id slug.
Tier and licensing rules enforced at create time (all return 403):
tier: systemcannot be created through the APItier: organizationrequires an Enterprise license- Community edition enforces a custom tenant-policy limit (currently 20 per tenant)
Response (201 Created):
{
"id": "01JTENANTPOLICY1234567890",
"policy_id": "custom_ab12cd34ef56",
"name": "Block Competitor Mentions",
"description": "Block queries mentioning competitor products",
"category": "security-admin",
"tier": "tenant",
"pattern": "(?i)(competitor-a|competitor-b|rival-product)",
"action": "block",
"severity": "medium",
"priority": 90,
"enabled": true,
"tags": ["competitive-intelligence", "sales"],
"tenant_id": "my-tenant",
"version": 1,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
System-tier policies cannot be created through this endpoint. Use it for tenant-tier policies in Community Edition and tenant or organization policies in Enterprise.
GET /api/v1/static-policies/{id}
Get a specific policy by ID.
Request:
curl http://localhost:8080/api/v1/static-policies/custom_ab12cd34ef56 \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (200 OK):
{
"id": "01JTENANTPOLICY1234567890",
"policy_id": "custom_ab12cd34ef56",
"name": "Block Competitor Mentions",
"description": "Block queries mentioning competitor products",
"category": "security-admin",
"tier": "tenant",
"pattern": "(?i)(competitor-a|competitor-b|rival-product)",
"action": "block",
"severity": "medium",
"enabled": true,
"priority": 90,
"tenant_id": "my-tenant",
"version": 3,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T12:00:00Z"
}
PUT /api/v1/static-policies/{id}
Update an existing policy.
Request:
curl -X PUT http://localhost:8080/api/v1/static-policies/custom_ab12cd34ef56 \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-H "X-User-ID: [email protected]" \
-d '{
"pattern": "(?i)(competitor-a|competitor-b|competitor-c|rival-product)",
"enabled": true,
"priority": 95
}'
Updatable fields: name, description, pattern, action, severity, priority, enabled, tags. Omitted fields are left unchanged.
Response (200 OK): the full updated policy object (same shape as GET /api/v1/static-policies/{id}), with version incremented.
{
"id": "01JTENANTPOLICY1234567890",
"policy_id": "custom_ab12cd34ef56",
"name": "Block Competitor Mentions",
"description": "Block queries mentioning competitor products",
"category": "security-admin",
"tier": "tenant",
"pattern": "(?i)(competitor-a|competitor-b|competitor-c|rival-product)",
"action": "block",
"severity": "medium",
"enabled": true,
"priority": 95,
"tenant_id": "my-tenant",
"version": 4,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T12:00:00Z"
}
System-tier policies cannot be modified or deleted through this endpoint (403). Create an override instead.
DELETE /api/v1/static-policies/{id}
Soft-delete a policy. The policy is disabled but retained for audit purposes.
Request:
curl -X DELETE http://localhost:8080/api/v1/static-policies/custom_ab12cd34ef56 \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (204 No Content):
No response body.
PATCH /api/v1/static-policies/{id}
Toggle the enabled status of a policy.
Request:
curl -X PATCH http://localhost:8080/api/v1/static-policies/custom_ab12cd34ef56 \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-d '{
"enabled": false
}'
Response (200 OK):
{
"id": "01JTENANTPOLICY1234567890",
"policy_id": "custom_ab12cd34ef56",
"name": "Block Competitor Mentions",
"description": "Block queries mentioning competitor products",
"category": "security-admin",
"tier": "tenant",
"pattern": "(?i)(competitor-a|competitor-b|rival-product)",
"action": "block",
"severity": "medium",
"enabled": false,
"priority": 90,
"tenant_id": "my-tenant",
"version": 4,
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T15:00:00Z"
}
The response is the full updated policy object. System-tier policies cannot be disabled through this endpoint (403); use an override's enabled_override instead.
Policy Testing
POST /api/v1/static-policies/test
Test a pattern against sample inputs without creating a policy.
Request:
curl -X POST http://localhost:8080/api/v1/static-policies/test \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-d '{
"pattern": "(?i)select\\s+(.*)\\s+from",
"inputs": [
"SELECT * FROM users WHERE id = 1",
"What is the weather today?"
]
}'
pattern is required. Provide the sample strings as an inputs array, or a single string in input (backward-compatible alias, used only when inputs is empty).
Response (200 OK):
{
"valid": true,
"matches": [
{
"input": "SELECT * FROM users WHERE id = 1",
"matched": true,
"groups": ["*"]
},
{
"input": "What is the weather today?",
"matched": false
}
],
"pattern": "(?i)select\\s+(.*)\\s+from",
"inputs": [
"SELECT * FROM users WHERE id = 1",
"What is the weather today?"
]
}
groups contains the values of the pattern's capture groups for matched inputs (omitted when the pattern has no capture groups or the input did not match). An uncompilable pattern returns 200 with valid: false and an error message.
GET /api/v1/static-policies/effective
Get effective policies for a tenant, including system policies and tenant overrides.
Request:
curl http://localhost:8080/api/v1/static-policies/effective \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (200 OK):
{
"static": [
{
"id": "01JSTATICPOLICY1234567890",
"policy_id": "sys_sqli_union_select",
"name": "UNION SELECT Detection",
"description": "Detects UNION-based SQL injection attempts",
"category": "security-sqli",
"tier": "system",
"pattern": "(?i)union\\s+(all\\s+)?select",
"action": "block",
"severity": "critical",
"priority": 100,
"enabled": true,
"tenant_id": "",
"version": 1,
"has_override": false,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
},
{
"id": "01JSTATICPOLICY0987654321",
"policy_id": "sys_pii_credit_card",
"name": "PII - Credit Card Detection",
"description": "Detects credit card numbers",
"category": "pii-global",
"tier": "system",
"pattern": "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b",
"action": "warn",
"severity": "high",
"priority": 90,
"enabled": true,
"tenant_id": "",
"version": 1,
"has_override": true,
"override_action": "block",
"override_enabled": true,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
],
"dynamic": [],
"tenant_id": "my-tenant",
"computed_at": "2025-01-02T12:00:00Z"
}
Override fields on each entry: has_override is always present; override_action, override_enabled, override_expires_at, and override_reason appear only when an override exists. organization_id at the top level appears only when the request carries organization context. The dynamic array is always empty on this endpoint — effective dynamic policies come from GET /api/v1/dynamic-policies/effective on the Orchestrator side.
Version History
GET /api/v1/static-policies/{id}/versions
Get version history for a policy.
Request:
curl http://localhost:8080/api/v1/static-policies/custom_ab12cd34ef56/versions \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (200 OK):
{
"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_by": "[email protected]",
"changed_at": "2025-01-02T14:00:00Z"
},
{
"id": "7a03d34e-bdd9-356e-98c3-80a1b2c3d4e5",
"policy_id": "custom_ab12cd34ef56",
"version": 3,
"snapshot": {
"name": "Block Competitor Mentions",
"pattern": "(?i)(competitor-a|competitor-b|rival-product)",
"action": "block",
"enabled": true
},
"change_type": "update",
"change_summary": "Policy updated",
"changed_by": "[email protected]",
"changed_at": "2025-01-02T12:00:00Z"
}
],
"count": 2
}
Each entry carries the complete policy state at that version in snapshot (the example above is abridged), plus change_type (create, update, enable, disable, delete), change_summary, changed_by, and changed_at.
Community edition returns the last 5 versions; Enterprise returns the full history.
Policy Overrides (Enterprise)
Tenant-specific overrides allow customizing system policy behavior without modifying the base policy. Only system-tier policies can be overridden.
POST /api/v1/static-policies/{id}/override
Create an override for a system policy.
Request:
curl -X POST http://localhost:8080/api/v1/static-policies/sys_pii_credit_card/override \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-H "X-User-ID: [email protected]" \
-d '{
"action_override": "block",
"enabled_override": true,
"override_reason": "Financial services compliance requires blocking",
"expires_at": "2026-12-31T23:59:59Z"
}'
Request body: override_reason is required (400 when missing). action_override (one of block, require_approval, redact, warn, log), enabled_override, and expires_at are optional.
Response (201 Created):
{
"id": "3f2b8c1d-1111-4222-8333-944445555666",
"policy_id": "sys_pii_credit_card",
"policy_type": "static",
"tenant_id": "my-tenant",
"org_id": "my-tenant",
"action_override": "block",
"enabled_override": true,
"override_reason": "Financial services compliance requires blocking",
"expires_at": "2026-12-31T23:59:59Z",
"created_by": "[email protected]",
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
Error cases: 400 when override_reason is missing, 403 without an Enterprise license, 409 when an override already exists for the policy.
GET /api/v1/static-policies/{id}/override
Get the active override for a single policy. {id} may be the policy UUID or the human-readable slug.
Response: 200 with the override object shown above, or 404 when no override exists.
DELETE /api/v1/static-policies/{id}/override
Remove a policy override, reverting to system default.
Request:
curl -X DELETE http://localhost:8080/api/v1/static-policies/sys_pii_credit_card/override \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (204 No Content):
No response body. Returns 404 when no override exists.
GET /api/v1/static-policies/overrides
List all policy overrides for a tenant. Also available at the alias path GET /api/v1/policy-overrides (same handler, same auth).
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
include_expired | boolean | false | Include overrides whose expires_at has passed |
Request:
curl "http://localhost:8080/api/v1/static-policies/overrides?include_expired=true" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"
Response (200 OK):
{
"overrides": [
{
"id": "3f2b8c1d-1111-4222-8333-944445555666",
"policy_id": "sys_pii_credit_card",
"policy_type": "static",
"tenant_id": "my-tenant",
"org_id": "my-tenant",
"action_override": "block",
"enabled_override": true,
"override_reason": "Financial services compliance requires blocking",
"expires_at": "2026-12-31T23:59:59Z",
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
],
"count": 1
}
Policy Actions
| Action | Description |
|---|---|
block | Reject the request and return an error |
redact | Allow the request but redact matched content from the input or output |
warn | Allow the request but include a warning in the response |
require_approval | Pause the request and require human approval before proceeding |
log | Allow the request and log the match silently |
Error Responses
Errors are returned as {"error": {"code": <HTTP status>, "message": "..."}} — the code field is the numeric HTTP status, not a string constant.
| HTTP Status | When |
|---|---|
| 400 | Missing required field, invalid regex pattern, invalid category or tier, malformed JSON body, missing override_reason |
| 401 | Tenant could not be determined from the request credentials |
| 403 | Modifying/deleting a system policy, creating a system-tier policy, organization tier or overrides without Enterprise, Community tenant-policy limit reached |
| 404 | Policy or override does not exist |
| 409 | Override already exists for the policy |
| 500 | Internal error |
SDK Examples
Use the AxonFlow SDKs to manage system policies programmatically.
List Policies (Go)
enabled := true
policies, _ := client.ListStaticPolicies(&axonflow.ListStaticPoliciesOptions{
Enabled: &enabled,
Category: axonflow.CategorySecuritySQLI,
})
for _, p := range policies {
fmt.Printf("%s: %s (%s)\n", p.ID, p.Name, p.Action)
}
Create Custom Policy (Python)
from axonflow import CreateStaticPolicyRequest, PolicyCategory
policy = await client.create_static_policy(
CreateStaticPolicyRequest(
name="Block Competitor Mentions",
category=PolicyCategory.SECURITY_ADMIN,
pattern=r"(?i)(competitor-a|competitor-b)",
action="block",
)
)
Test Pattern (TypeScript)
const result = await client.testPattern("(?i)(competitor-a|competitor-b)", [
"Check competitor-a pricing",
"What is the weather today?",
]);
console.log(`Valid: ${result.valid}`);
Community Examples
- Policy Examples - System and tenant policy patterns
- PII Detection - PII detection patterns
- SQL Injection - SQLi prevention examples
Next Steps
- Agent Endpoints - Policy enforcement API
- Security - PII Detection - PII detection patterns
- Security - SQL Injection - SQLi prevention
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
