Skip to main content

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 supplied X-Tenant-ID header 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_id in the request body (not a header)
  • X-User-ID is 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:

ParameterTypeDefaultDescription
enabledboolean(none — returns all)Filter by enabled status
categorystring(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
tierstring(none — returns all)Filter by tier: system, organization, or tenant
searchstring(none — returns all)Case-insensitive match on name or description
pageinteger1Page number
limitinteger20 (max: 100)Preferred items-per-page parameter
page_sizeintegerDeprecatedBackward-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:

FieldTypeRequiredDefaultDescription
namestringYesPolicy display name
descriptionstringNoPolicy description
categorystringYesOne 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
tierstringNotenantTier: tenant or organization via API (system is rejected with 403)
organization_idstringNoOrganization identifier; required when tier is organization
patternstringYesRE2 regex pattern to match (validated for syntax, length, and capture-group limits; invalid patterns return 400)
actionstringYesAction: block, require_approval, redact, warn, or log
severitystringNoSeverity: critical, high, medium, low. No server-side default is applied on create — set it explicitly
priorityintegerNo50Evaluation priority (higher runs earlier). Omitted or 0 is replaced with the default 50
enabledbooleanNofalseWhether policy is active
tagsarrayNoTags 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: system cannot be created through the API
  • tier: organization requires 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"
}
note

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:

ParameterTypeDefaultDescription
include_expiredbooleanfalseInclude 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

ActionDescription
blockReject the request and return an error
redactAllow the request but redact matched content from the input or output
warnAllow the request but include a warning in the response
require_approvalPause the request and require human approval before proceeding
logAllow 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 StatusWhen
400Missing required field, invalid regex pattern, invalid category or tier, malformed JSON body, missing override_reason
401Tenant could not be determined from the request credentials
403Modifying/deleting a system policy, creating a system-tier policy, organization tier or overrides without Enterprise, Community tenant-policy limit reached
404Policy or override does not exist
409Override already exists for the policy
500Internal 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


Next Steps

Operational Readiness Checklist

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