Dynamic Policy API
Manage dynamic policies for runtime governance through the Orchestrator API. Dynamic policies use condition-based rules evaluated at request time for flexible, tenant-specific governance.
Overview
Dynamic policies differ from static policies:
| Feature | Static Policies | Dynamic Policies |
|---|---|---|
| Structure | Regex patterns | Conditions + Actions |
| Location | Agent | Orchestrator |
| Use Case | Pattern matching (PII, SQLi) | Business logic, risk, cost |
| Scope | Request content | User, context, cost, risk |
Base URL: http://localhost:8081 (Orchestrator)
Policy Types
| Type | Description |
|---|---|
content | Evaluate based on query/response content |
user | Evaluate based on user attributes |
risk | Evaluate based on risk scores |
cost | Evaluate based on cost estimates |
Policy Tiers
| Tier | Description |
|---|---|
system | AxonFlow-managed, immutable |
organization | Organization-wide (Enterprise) |
tenant | Team-specific policies |
Endpoints
GET /api/v1/policies
List all dynamic policies for a tenant with optional filtering.
Request:
curl "http://localhost:8081/api/v1/policies?type=user&enabled=true&page=1&page_size=20" \
-H "X-Tenant-ID: my-tenant"
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Filter by policy type (content, user, risk, cost) |
category | string | Filter by category (dynamic-risk, dynamic-compliance, etc.) |
tier | string | Filter by tier (system, organization, tenant) |
enabled | boolean | Filter by enabled status |
search | string | Search in name and description |
include_deleted | boolean | Include soft-deleted policies |
sort_by | string | Sort field (name, created_at, updated_at) |
sort_dir | string | Sort direction (asc, desc) |
page | integer | Page number (default: 1) |
page_size | integer | Items per page (default: 20, max: 100) |
Response (200 OK):
{
"policies": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"description": "Limit external API calls per user per hour",
"type": "user",
"category": "dynamic-risk",
"tier": "tenant",
"conditions": [
{
"field": "user.department",
"operator": "equals",
"value": "engineering"
},
{
"field": "cost_estimate",
"operator": "greater_than",
"value": 10.0
}
],
"actions": [
{
"type": "alert",
"config": {
"channel": "slack",
"message": "High cost request from engineering"
}
}
],
"priority": 100,
"enabled": true,
"version": 3,
"tenant_id": "my-tenant",
"created_by": "[email protected]",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 15,
"total_pages": 1
}
}
POST /api/v1/policies
Create a new dynamic policy.
Request:
curl -X POST http://localhost:8081/api/v1/policies \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: my-tenant" \
-H "X-User-ID: [email protected]" \
-d '{
"name": "Block High-Cost Requests",
"description": "Block requests estimated to cost more than $5",
"type": "cost",
"category": "dynamic-cost",
"tier": "tenant",
"conditions": [
{
"field": "cost_estimate",
"operator": "greater_than",
"value": 5.0
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Request exceeds cost limit of $5"
}
}
],
"priority": 100,
"enabled": true,
"tags": ["cost-control", "budget"]
}'
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Policy display name (max 255 chars) |
description | string | No | Policy description |
type | string | Yes | Policy type: content, user, risk, cost |
category | string | No | Category: dynamic-risk, dynamic-compliance, etc. |
tier | string | No | Tier: organization, tenant (default: tenant) |
conditions | array | Yes | Condition rules to evaluate |
actions | array | Yes | Actions to execute when conditions match |
priority | integer | No | Evaluation priority (higher = first) |
enabled | boolean | No | Whether policy is active (default: true) |
tags | array | No | Tags for filtering |
Condition Object:
| Field | Type | Description |
|---|---|---|
field | string | Field to evaluate (see Available Fields) |
operator | string | Comparison operator (see Operators) |
value | any | Value to compare against |
Action Object:
| Field | Type | Description |
|---|---|---|
type | string | Action type: block, redact, alert, log, route, modify_risk |
config | object | Action-specific configuration |
Response (201 Created):
{
"policy": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Block High-Cost Requests",
"description": "Block requests estimated to cost more than $5",
"type": "cost",
"category": "dynamic-cost",
"tier": "tenant",
"conditions": [
{
"field": "cost_estimate",
"operator": "greater_than",
"value": 5.0
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Request exceeds cost limit of $5"
}
}
],
"priority": 100,
"enabled": true,
"version": 1,
"tenant_id": "my-tenant",
"tags": ["cost-control", "budget"],
"created_by": "[email protected]",
"created_at": "2025-01-02T10:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
}
GET /api/v1/policies/{id}
Get a specific policy by ID.
Request:
curl http://localhost:8081/api/v1/policies/550e8400-e29b-41d4-a716-446655440001 \
-H "X-Tenant-ID: my-tenant"
Response (200 OK):
{
"policy": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"description": "Limit external API calls per user per hour",
"type": "user",
"category": "dynamic-risk",
"tier": "tenant",
"conditions": [
{
"field": "user.role",
"operator": "not_equals",
"value": "admin"
},
{
"field": "request_type",
"operator": "equals",
"value": "mcp_query"
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Non-admin users cannot execute MCP queries"
}
}
],
"priority": 100,
"enabled": true,
"version": 3,
"tenant_id": "my-tenant",
"created_by": "[email protected]",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-02T10:00:00Z"
}
}
PUT /api/v1/policies/{id}
Update an existing policy. Creates a new version automatically.
Request:
curl -X PUT http://localhost:8081/api/v1/policies/550e8400-e29b-41d4-a716-446655440001 \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: my-tenant" \
-H "X-User-ID: [email protected]" \
-d '{
"description": "Updated: Allow admins to bypass",
"conditions": [
{
"field": "user.role",
"operator": "not_in",
"value": ["admin", "superadmin"]
}
],
"priority": 150
}'
Request Body:
All fields are optional. Only provided fields will be updated.
| Field | Type | Description |
|---|---|---|
name | string | Policy display name |
description | string | Policy description |
type | string | Policy type |
category | string | Policy category |
conditions | array | Condition rules |
actions | array | Action rules |
priority | integer | Evaluation priority |
enabled | boolean | Whether policy is active |
tags | array | Tags for filtering |
Response (200 OK):
{
"policy": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"description": "Updated: Allow admins to bypass",
"type": "user",
"category": "dynamic-risk",
"tier": "tenant",
"conditions": [
{
"field": "user.role",
"operator": "not_in",
"value": ["admin", "superadmin"]
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Non-admin users cannot execute MCP queries"
}
}
],
"priority": 150,
"enabled": true,
"version": 4,
"tenant_id": "my-tenant",
"created_by": "[email protected]",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-02T12:00:00Z"
}
}
DELETE /api/v1/policies/{id}
Delete a policy permanently.
Request:
curl -X DELETE http://localhost:8081/api/v1/policies/550e8400-e29b-41d4-a716-446655440001 \
-H "X-Tenant-ID: my-tenant" \
-H "X-User-ID: [email protected]"
Response (204 No Content):
No response body on success.
POST /api/v1/policies/{id}/test
Test a policy against sample input without affecting production.
Request:
curl -X POST http://localhost:8081/api/v1/policies/550e8400-e29b-41d4-a716-446655440001/test \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: my-tenant" \
-d '{
"query": "Run database migration",
"user": {
"id": "user_123",
"email": "[email protected]",
"role": "developer"
},
"request_type": "mcp_query",
"context": {
"connector": "postgresql"
}
}'
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Sample query to evaluate |
user | object | No | User attributes for evaluation |
request_type | string | No | Request type being simulated |
context | object | No | Additional context for evaluation |
Response (200 OK):
{
"matched": true,
"blocked": true,
"actions": [
{
"type": "block",
"config": {
"message": "Non-admin users cannot execute MCP queries"
},
"message": "Request blocked by policy"
}
],
"explanation": "Condition matched: user.role (developer) not_equals admin",
"eval_time_ms": 1.5
}
GET /api/v1/policies/{id}/versions
Get version history for a policy.
Request:
curl http://localhost:8081/api/v1/policies/550e8400-e29b-41d4-a716-446655440001/versions \
-H "X-Tenant-ID: my-tenant"
Response (200 OK):
{
"versions": [
{
"version": 4,
"snapshot": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"conditions": [{"field": "user.role", "operator": "not_in", "value": ["admin", "superadmin"]}],
"actions": [{"type": "block", "config": {"message": "..."}}],
"priority": 150,
"enabled": true
},
"changed_by": "[email protected]",
"changed_at": "2025-01-02T12:00:00Z",
"change_type": "update",
"change_summary": "Updated conditions to exclude superadmin"
},
{
"version": 3,
"snapshot": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"conditions": [{"field": "user.role", "operator": "not_equals", "value": "admin"}],
"actions": [{"type": "block", "config": {"message": "..."}}],
"priority": 100,
"enabled": true
},
"changed_by": "[email protected]",
"changed_at": "2025-01-02T10:00:00Z",
"change_type": "update",
"change_summary": "Initial version"
}
]
}
Bulk Operations
POST /api/v1/policies/import
Import multiple policies from JSON. Useful for environment migration.
Request:
curl -X POST http://localhost:8081/api/v1/policies/import \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: my-tenant" \
-H "X-User-ID: [email protected]" \
-d '{
"policies": [
{
"name": "Block Non-Admins from Delete",
"type": "user",
"conditions": [{"field": "user.role", "operator": "not_equals", "value": "admin"}],
"actions": [{"type": "block", "config": {"message": "Only admins can delete"}}],
"priority": 100,
"enabled": true
},
{
"name": "Alert on High Cost",
"type": "cost",
"conditions": [{"field": "cost_estimate", "operator": "greater_than", "value": 10}],
"actions": [{"type": "alert", "config": {"channel": "slack"}}],
"priority": 50,
"enabled": true
}
],
"overwrite_mode": "skip"
}'
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
policies | array | Yes | Array of policies to import (max 100) |
overwrite_mode | string | No | Conflict handling: skip, overwrite, error (default: skip) |
Response (200 OK):
{
"created": 2,
"updated": 0,
"skipped": 0,
"errors": []
}
GET /api/v1/policies/export
Export all policies for a tenant as JSON.
Request:
curl http://localhost:8081/api/v1/policies/export \
-H "X-Tenant-ID: my-tenant"
Response (200 OK):
{
"policies": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Rate Limit External APIs",
"description": "Limit external API calls per user per hour",
"type": "user",
"category": "dynamic-risk",
"tier": "tenant",
"conditions": [{"field": "user.role", "operator": "not_equals", "value": "admin"}],
"actions": [{"type": "block", "config": {"message": "..."}}],
"priority": 100,
"enabled": true,
"version": 4
}
],
"exported_at": "2025-01-02T10:00:00Z",
"tenant_id": "my-tenant"
}
Condition Reference
Available Fields
| Field | Type | Description |
|---|---|---|
query | string | The user's query |
response | string | The LLM response |
user.email | string | User email |
user.role | string | User role |
user.department | string | User department |
user.tenant_id | string | User's tenant ID |
risk_score | number | Calculated risk score |
request_type | string | Type of request |
connector | string | MCP connector name |
cost_estimate | number | Estimated cost in USD |
Operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match | {"field": "user.role", "operator": "equals", "value": "admin"} |
not_equals | Not equal | {"field": "user.role", "operator": "not_equals", "value": "admin"} |
contains | String contains | {"field": "query", "operator": "contains", "value": "password"} |
not_contains | String doesn't contain | {"field": "query", "operator": "not_contains", "value": "test"} |
contains_any | Contains any of values | {"field": "query", "operator": "contains_any", "value": ["secret", "key"]} |
regex | Regular expression match | {"field": "query", "operator": "regex", "value": "\\d{4}-\\d{4}"} |
greater_than | Numeric greater than | {"field": "cost_estimate", "operator": "greater_than", "value": 5.0} |
less_than | Numeric less than | {"field": "risk_score", "operator": "less_than", "value": 50} |
in | Value in list | {"field": "user.role", "operator": "in", "value": ["admin", "superadmin"]} |
not_in | Value not in list | {"field": "user.department", "operator": "not_in", "value": ["HR", "Legal"]} |
Action Types
| Type | Description | Config |
|---|---|---|
block | Block the request | {"message": "Request blocked"} |
redact | Redact sensitive content | {"pattern": "\\d{4}-\\d{4}", "replacement": "[REDACTED]"} |
alert | Send alert notification | {"channel": "slack", "message": "Alert!"} |
log | Log the event | {"level": "warn", "include_query": true} |
route | Route to different provider | {"provider": "anthropic"} |
modify_risk | Adjust risk score | {"delta": 10} |
Example Policies
Block non-admin MCP queries:
{
"name": "Block Non-Admin MCP",
"type": "user",
"conditions": [
{"field": "user.role", "operator": "not_equals", "value": "admin"},
{"field": "request_type", "operator": "equals", "value": "mcp_query"}
],
"actions": [
{"type": "block", "config": {"message": "Only admins can run MCP queries"}}
]
}
Alert on high-cost requests:
{
"name": "High Cost Alert",
"type": "cost",
"conditions": [
{"field": "cost_estimate", "operator": "greater_than", "value": 5.0}
],
"actions": [
{"type": "alert", "config": {"channel": "slack", "message": "High cost request: ${{cost_estimate}}"}}
]
}
Error Responses
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | INVALID_JSON | Invalid JSON in request body |
| 400 | VALIDATION_ERROR | Policy validation failed |
| 401 | UNAUTHORIZED | Missing tenant ID |
| 403 | TIER_LIMIT_EXCEEDED | Policy limit exceeded for tier |
| 404 | NOT_FOUND | Policy does not exist |
| 500 | INTERNAL_ERROR | Internal server error |
Next Steps
- Static Policy API - Pattern-based enforcement
- Agent Endpoints - Policy evaluation API
- SDK Documentation - Language-specific SDKs