Download OpenAPI specification:Download
REST API for the AxonFlow Agent service - Authentication, Authorization, and Static Policy Enforcement Gateway.
The Agent serves as the entry point for all client requests, providing:
The Agent is the single entry point for all client traffic (ADR-024):
clients never call the Orchestrator directly — it is an internal service
and must not be exposed. Every /api/v1/* endpoint is reachable
through the Agent. This spec documents the endpoints the Agent serves
natively; endpoints the Agent transparently proxies to the Orchestrator
(audit, decisions, overrides, plans/workflows, cost/budgets/usage,
connectors, LLM-provider management, evidence, and the
regulatory-compliance families — EU AI Act, SEBI, RBI, OJK/BI/UU-PDP,
MAS FEAT) are documented in their canonical specs: orchestrator-api.yaml
for most families, masfeat-api.yaml for MAS FEAT, and policy-api.yaml
for dynamic policies. They are still called via the Agent host and port.
The EU AI Act family additionally keeps pointer entries in this spec, and
a few agent-native subroutes under otherwise-proxied prefixes (audit
verification, connector cache refresh) are documented here as well.
All endpoints require authentication via:
Authorization: Basic base64(clientId:clientSecret): OAuth2-style client credentials (recommended)user_token: JWT token for user identification (in request body)Note: clientSecret is optional for community/self-hosted deployments. clientId is recommended for request identification.
DEPLOYMENT_MODE=saas)DEPLOYMENT_MODE=enterprise)DEPLOYMENT_MODE=community) - bypasses license validationReturns service health status. During startup, returns status: starting.
Once fully initialized, returns status: healthy.
This endpoint responds immediately even during initialization, allowing ECS/ALB health checks to pass while the service starts up.
{- "status": "healthy",
- "service": "axonflow-agent",
- "timestamp": "2025-01-15T10:30:00Z",
- "version": "1.0.0"
}Main entry point for Proxy Mode. The Agent:
One pass since v11.0.0 (#4253). The second pass that used to
follow an anchored approval - the tier engine, over static_policies'
stored action column and an organization's legacy per-policy
overrides - is retired. A legacy per-policy override (block or
require_approval) no longer decides this route; the effective-policies
read (GET /api/v1/static-policies/effective) keeps showing it under
its deprecation (PRD v11 §1.11). A shipped system control whose stored
action is block is still refused here, by the anchored engine, naming
its policy. The agent's pass resolves no governance segments, so a
segment-store outage refuses nothing at that pass (a request it
forwards to the orchestrator's /api/v1/process or
/api/v1/plan/execute is decided there by the anchored engine too,
whose facts resolve the user's segments and refuse when that fails), and
the pass never holds a request for approval: an anchored approval
challenge is a refusal.
Every policy verdict names the engine that decided it (engine,
always anchored), the type of principal it decided for
(subject_type) and the digest of the policy bundle
(policy_bundle).
The LLM response is decided too. A forwarded request's response
is decided by the orchestrator response plane, on the anchored
engine, and response_plane names that decision. A response it
withholds answers success: false and blocked: true, and is not
counted against the client's circuit breaker.
Use this endpoint when you want AxonFlow to intercept and process all LLM requests.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| X-Client-Secret | string Deprecated DEPRECATED: Use Basic authentication instead.
Legacy client secret header. Prefer using |
| query required | string [ 1 .. 100000 ] characters The query or prompt to process |
| user_token | string JWT token for user authentication |
| client_id required | string Registered client application ID |
| request_type | string Enum: "sql" "llm_chat" "rag_search" "mcp-query" "multi-agent-plan" Type of request:
|
| skip_llm | boolean Default: false Skip LLM calls (for testing) |
object Additional context for request processing | |
Array of objects Optional multimodal payload accompanying the query
(images, documents, etc.). Consumed by the platform's
media-governance code path; per-item shape is
|
{- "query": "Show sales data for last quarter",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "client_id": "travel-app-prod",
- "request_type": "sql",
- "context": {
- "connector": "postgres"
}
}{- "success": true,
- "data": {
- "response": "Here is the summary of customer feedback..."
}, - "result": "Flight options found: UA123, AA456, DL789",
- "plan_id": "plan_1234567890_abc123",
- "metadata": {
- "tasks_executed": 3,
- "execution_time_ms": 2500
}, - "policy_info": {
- "policies_evaluated": [
- "pii-detection",
- "rate-limit"
], - "static_checks": [
- "ssn_pattern",
- "credit_card"
], - "processing_time": "45.2ms",
- "tenant_id": "tenant-123"
}, - "engine": "anchored",
- "subject_type": "User",
- "policy_bundle": "sha256:<the digest of the bundle that decided the request>",
- "response_plane": {
- "engine": "anchored",
- "subject_type": "Client",
- "policy_bundle": "sha256:<the digest of the bundle that decided the response>",
- "verdict": "allowed"
}
}Previews what POST /api/request would decide for the authenticated
credential, through the same pass: the same detector evaluation and
the same anchored enforcing seam. A policy is tested against the
engine that enforces it (PRD v11 §1 item 10). It records no decision:
no audit_logs row, no enforce-decision count, no circuit-breaker
violation and no signed decision. The shared engine's evaluation that
feeds the pass still counts in its metrics, and where the engine
carries an audit queue a matched block is logged to
policy_violations, as before v11.0.0.
Changed in v11.0.0 (#4253). It used to preview two legacy passes
and resolve the governance segments of the body's user_email. It now
decides for the credential that authenticated the call
(subject_type Client): on Community as /api/request decides a
request that carries no user token; on Enterprise, where
/api/request requires a user token, as the credential's service
identity, the way /api/v1/decide decides a token-less caller - and
it is refused 401 where the organization requires a user token, as
decide refuses it. user_email is accepted and ignored (a body field is not a
principal), segments_resolved is no longer in the response, and the
response gains engine, subject_type and policy_bundle. Where the
anchored engine cannot decide, the preview answers 503, as the route
does.
| query required | string Query to preview |
| user_email | string Accepted and ignored since v11.0.0 (#4253): the preview is decided for the authenticated credential. |
| request_type | string Request type |
{- "query": "DROP TABLE customers",
- "request_type": "sql"
}{- "blocked": true,
- "reason": "explicit_constraint",
- "triggered_policies": [
- "corpus:static_policies:drop__table__prevention"
], - "checks_performed": [
- "shared_policy_engine"
], - "processing_time_ms": 3,
- "engine": "anchored",
- "subject_type": "Client",
- "policy_bundle": "sha256:<the digest of the bundle that decided the preview>"
}[- {
- "id": "string",
- "name": "string",
- "org_id": "string",
- "tenant_id": "string",
- "permissions": [
- "string"
], - "rate_limit": 0,
- "enabled": true,
- "license_tier": "Community",
- "license_expiry": "2019-08-24T14:15:22Z"
}
]Register a new client application
| id | string Unique client identifier |
| name | string Client application name |
| org_id | string Organization ID for usage tracking |
| tenant_id | string Tenant ID for multi-tenancy |
| permissions | Array of strings Granted permissions |
| rate_limit | integer Requests per minute limit |
| enabled | boolean Whether client is active |
| license_tier | string Enum: "Community" "starter" "professional" "enterprise" License tier |
| license_expiry | string <date-time> When license expires |
{- "id": "string",
- "name": "string",
- "org_id": "string",
- "tenant_id": "string",
- "permissions": [
- "string"
], - "rate_limit": 0,
- "enabled": true,
- "license_tier": "Community",
- "license_expiry": "2019-08-24T14:15:22Z"
}{- "id": "string",
- "name": "string",
- "org_id": "string",
- "tenant_id": "string",
- "permissions": [
- "string"
], - "rate_limit": 0,
- "enabled": true,
- "license_tier": "Community",
- "license_expiry": "2019-08-24T14:15:22Z"
}Gateway Mode Step 1: Call this endpoint before making your own LLM API call.
The Agent validates the request against policies and returns:
verdict - the canonical allow | deny, the same vocabulary
POST /api/v1/decide uses. Read this one. Since v11 the pre-check
never holds a request: a require_approval policy is a deny.approved: true if the request is allowed (retained)decision_id — the decision identifier. Use it for the subsequent
audit call, AND for GET /api/v1/decisions/{decision_id}/explain,
which is keyed on it.context_id — a deprecated alias of decision_id, same valueapproved_data from MCP connectorsapproved_data is only prefetched for clean approvals (#2868):
when the request is blocked, requires HITL approval, or requires
redaction, connector prefetch is skipped and approved_data is
never populated — governed data is not fetched for a request that
may not proceed.
Context expires after 5 minutes.
1. SDK calls pre-check → gets context_id, approved=true
2. SDK makes direct LLM call (OpenAI, Anthropic, etc.)
3. SDK calls audit with context_id and response metadata
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| query required | string non-empty Query to validate |
| user_token | string JWT token for user authentication |
| client_id required | string Client application ID |
| data_sources | Array of strings MCP connectors to fetch data from |
object Additional context |
{- "query": "What is the customer's order status?",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "client_id": "customer-portal"
}{- "context_id": "ctx_abc123def456",
- "approved": true,
- "policies": [
- "pii-detection",
- "rate-limit"
], - "rate_limit": {
- "limit": 1000,
- "remaining": 995,
- "reset_at": "2025-01-15T11:00:00Z"
}, - "expires_at": "2025-01-15T10:35:00Z"
}Gateway Mode Step 2: Call this endpoint after your LLM API call completes.
Records:
Requires a valid context_id from pre-check (not expired).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| context_id required | string Context ID from pre-check |
| client_id required | string Client application ID |
| response_summary | string <= 500 characters Brief summary of LLM response (for audit) |
| provider required | string Enum: "openai" "azure-openai" "anthropic" "bedrock" "ollama" "gemini" LLM provider name |
| model required | string Model identifier |
required | object (TokenUsage) |
| latency_ms | integer LLM call latency in milliseconds |
object Additional metadata for audit |
{- "context_id": "ctx_abc123def456",
- "client_id": "travel-app",
- "response_summary": "Found 5 flights matching criteria",
- "provider": "openai",
- "model": "gpt-4",
- "token_usage": {
- "prompt_tokens": 150,
- "completion_tokens": 200,
- "total_tokens": 350
}, - "latency_ms": 1250,
- "metadata": {
- "request_type": "travel_search",
- "cache_hit": false
}
}{- "success": true,
- "audit_id": "aud_xyz789"
}Synchronous policy decision endpoint (ADR-056). Called by an
infrastructure gateway acting as a Policy Enforcement Point (PEP);
AxonFlow returns a verdict (allow or deny) and
the PEP enforces it. Same shared-policy engine as Gateway Mode
pre-check; difference is the caller.
Decision Mode endpoint (ADR-056 / epic #2426). The customer's
infrastructure gateway (Policy Enforcement Point) calls this
endpoint per request to get a verdict (allow or deny) and
enforces the result. AxonFlow is consulted,
never on the traffic path.
The shared-policy engine behind this endpoint is the same engine
that backs Gateway Mode's POST /api/policy/pre-check. The
difference is the caller: Gateway Mode is called by application
code; Decision Mode is called by an infrastructure gateway.
M1 scope: static policies only (PII detection, SQL injection, dangerous patterns, RBI India PII, compliance categories) to keep the inline RPC budget in single-digit milliseconds. Dynamic/custom policy support is M2 scope per the epic.
OTel trace correlation: the response carries a W3C-compatible
32-hex trace_id. When the caller passes a traceparent header,
its trace-id is reused so multi-gateway-layer decisions stitch
into one end-to-end trace. Each decision also emits an OpenTelemetry
span on the axonflow.agent.decision tracer.
Available at all tiers (Community through Enterprise) — the policy engine is the same one Gateway Mode uses.
| X-Axonflow-Client | string Example: mcp-proxy/0.3.1 Optional client-version telemetry header ( |
| X-Axonflow-PEP-Handshake | string <= 4096 characters The ADR-065 PEP capability handshake: base64url of a compact JSON
document in which an external enforcement point declares what it is and
which obligations it can discharge. See Absent is the default and changes nothing. A caller that omits the header takes byte-for-byte the path it took before this header existed. A header that is PRESENT and cannot be read is refused, never
treated as absent: degrading a malformed declaration to "legacy caller"
would go on handing an enforcement point obligations it had just said it
cannot discharge. The refusal is Present more than once is refused: RFC 7230 permits an intermediary to join repeated field lines with a comma, and a comma is outside the base64 alphabet, so a joined pair can only decode to malformed. That is why the document is base64 rather than raw JSON, which would join into something a lenient parser might accept. When a decision carries a mandatory obligation the declared set does
not cover, the request is answered |
| traceparent | string Example: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 W3C trace-context header. When present and valid, the trace-id is reused in the response so multi-layer decisions correlate into one end-to-end trace. |
| stage required | string Enum: "llm" "tool" "agent" Which gateway layer is calling. Maps to ADR-056's three-layer reference architecture (agent / MCP / LLM). |
object (DecisionCallerIdentity) Gateway-asserted caller identity. | |
object (DecisionTarget) What the gateway is about to call. | |
| query required | string non-empty The request body / prompt / statement being decided on. |
| user_token | string Optional end-user JWT for audit identity. PEP gateways are typically services and may omit this field -- in enterprise mode the platform synthesizes a service identity for the audit row when no token is supplied. Supplying a token gets the validated-user record on the audit row instead. |
object Optional caller-supplied context (string values) that AxonFlow
propagates end-to-end into the decision audit record + the OTel
decision span, so a SIEM can correlate the decision with upstream
logs (e.g. by session_id). Intended for infrastructure-gateway
audit headers such as Only keys matching the server's allowlist
( | |
| fulfillment_capabilities | Array of strings Items Enum: "request_body_redaction" "request_header_mutation" What this PEP's seam can mechanically do to a request before
forwarding it. A different axis from the capability handshake, which
declares which OBLIGATIONS the enforcement point can discharge:
Three wire states, three meanings:
Unknown values are ignored, never an error and never a block, so an older platform meeting a newer PEP's vocabulary degrades instead of failing. |
{- "stage": "llm",
- "caller_identity": {
- "gateway_id": "llm-gateway-01",
- "tenant_id": "acme-prod"
}, - "target": {
- "type": "llm",
- "model": "gpt-4o",
- "provider": "openai"
}, - "query": "What is the customer's order status?"
}{- "verdict": "allow",
- "decision_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
- "trace_id": "0af7651916cd43dd8448eb211c80319c",
- "stage": "llm",
- "reasons": [ ],
- "obligations": [ ],
- "evaluated_policies": [ ],
- "expires_at": "2026-05-23T10:35:00Z"
}Returns the MCP connectors the authenticated tenant may reach,
with their health status. Deployment-shared connectors (those
registered under the wildcard tenancy *) are included for every
tenant; another tenant's connectors are not
(platform/agent/mcp_handler.go:612-620).
Connectors provide access to external data sources:
Authentication. Wrapped in apiAuthMiddleware
(platform/agent/mcp_handler.go:581); the tenancy comes from the
credential, never from a caller-supplied header or path segment.
Registered for GET only — apiAuthMiddleware forwards CORS
preflights unauthenticated, so registering OPTIONS would reach the
handler with no identity in context.
(Behaviour change in #3067: this route was previously registered with no auth middleware and returned every tenant's connector names, types, versions, capabilities, health and raw driver error strings to an anonymous caller.)
{- "connectors": [
- {
- "name": "postgres_main",
- "type": "postgres",
- "version": "1.0.0",
- "healthy": true,
- "latency_ms": 5,
- "capabilities": [
- "query",
- "execute"
]
}, - {
- "name": "amadeus",
- "type": "amadeus",
- "version": "1.0.0",
- "healthy": true,
- "latency_ms": 120,
- "capabilities": [
- "search_flights",
- "search_hotels"
]
}
], - "count": 2
}Returns health status for one of the authenticated tenant's connectors (or a deployment-shared one).
Naming another tenant's connector returns the same 404 as a
nonexistent one — there is no existence oracle, and no live
connection is opened with the other tenant's credentials
(platform/agent/mcp_handler.go:665-676).
Authentication. Wrapped in apiAuthMiddleware
(platform/agent/mcp_handler.go:584), GET only.
(Behaviour change in #3067: this route was previously registered with no auth middleware, so an anonymous caller could name any tenant's connector and have the agent open a live connection with that tenant's decrypted credentials.)
| name required | string Example: postgres_main Connector name. Resolved within the authenticated tenancy, with a
fallback to the deployment-shared ( |
{- "healthy": true,
- "latency_ms": 5,
- "last_check": "2025-01-15T10:30:00Z"
}Execute a read-only query via an MCP connector.
This follows the MCP Resource pattern for data retrieval.
For write operations, use /mcp/tools/execute.
All MCP queries are automatically logged to the mcp_query_audits table with:
Each audit entry includes audit_id for correlation with SDK PolicyInfo.
Statement content is stored as SHA256 hash for privacy.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| client_id required | string |
| license_key | string Can also be provided in X-License-Key header |
| user_token | string |
| connector required | string Connector name |
| operation | string Operation name (for API connectors like Amadeus) |
| statement | string SQL/CQL statement (for database connectors) |
object Query parameters | |
| limit | integer Maximum rows to return |
| timeout | string Timeout duration (e.g., "10s") |
{- "client_id": "analytics-app",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "connector": "postgres_main",
- "statement": "SELECT * FROM orders WHERE status = $1",
- "parameters": {
- "1": "completed"
}, - "limit": 100,
- "timeout": "10s"
}{- "success": true,
- "connector": "amadeus",
- "data": [
- {
- "flight_number": "UA123",
- "departure": "2025-01-20T08:00:00Z",
- "arrival": "2025-01-20T11:00:00Z",
- "price": 299.99
}
], - "row_count": 5,
- "duration_ms": 450,
- "engine": "anchored",
- "subject_type": "User",
- "policy_bundle": "sha256:4b8f2a7c9e1d3f5a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a",
- "policy_packs": [
- "rbi@sha256:7d1e9a3c5b7f9d1e3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f"
]
}Execute a write command via an MCP connector.
This follows the MCP Tool pattern for data modification.
For read operations, use /mcp/resources/query.
All MCP execute operations are automatically logged to the mcp_query_audits table with:
Each audit entry includes audit_id for correlation. Operation type (INSERT, UPDATE, DELETE)
is stored in the operation field.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| client_id required | string |
| license_key | string |
| user_token | string |
| connector required | string |
| operation | string |
| action required | string Enum: "INSERT" "UPDATE" "DELETE" |
| statement | string |
object | |
| timeout | string |
{- "client_id": "order-service",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "connector": "postgres_main",
- "action": "UPDATE",
- "statement": "UPDATE orders SET status = $1 WHERE id = $2",
- "parameters": {
- "1": "shipped",
- "2": "ord-12345"
}, - "timeout": "5s"
}{- "success": true,
- "connector": "postgres_main",
- "rows_affected": 1,
- "duration_ms": 15,
- "message": "Update successful",
- "engine": "anchored",
- "subject_type": "User",
- "policy_bundle": "sha256:4b8f2a7c9e1d3f5a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a",
- "policy_packs": [
- "rbi@sha256:7d1e9a3c5b7f9d1e3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f"
]
}Validate an MCP request (query or command) against configured policies without executing it.
This endpoint enables external orchestrators (LangGraph, CrewAI, custom pipelines) to use AxonFlow as a policy gate while managing MCP connector execution themselves.
The anchored decision engine (ADR-065) decides the request from the organization's active typed policy document: the system detectors (SQL injection, dangerous queries, PII) run over the statement and its parameters, and the engine's verdict is the response. Tenant dynamic policies no longer decide MCP requests (PRD v11 §1.2).
If the engine denies the request, the response returns allowed: false with a block_reason. A request the engine cannot decide is refused with 503; it is never let through (PRD v11 §1.7).
Use check-input + check-output when your orchestrator manages MCP execution natively.
Use /mcp/resources/query or /mcp/tools/execute when you want AxonFlow to handle both
policy enforcement and connector execution.
All check-input evaluations are logged to the mcp_query_audits table with
operation: "check-input" for compliance tracking. Audit entries include
parameters_hash (SHA-256) and parameter_count for forensic analysis.
| Idempotency-Key | string [ 1 .. 256 ] characters ^[A-Za-z0-9_.:\-/]+$ Example: n8n-exec-abc123-node-Approve Optional per-request dedup token. When supplied on POST /api/v1/mcp/check-input, POST /api/v1/audit/tool-call, or POST /api/v1/hitl/queue, the platform caches the original response for 24h and returns it byte-for-byte on subsequent requests carrying the same key + same authenticated tenant + same endpoint. Format: 1-256 chars, Cache rules: 2xx + 4xx responses are cached; 5xx is NOT cached so the
caller's retry can hit a fresh attempt. A cache hit returns the original
response plus an Cross-tenant collisions are impossible: tenant_id participates in the primary key + an RLS policy on the storage table. Two tenants using the same key value get distinct rows. |
| client_id | string Client identifier (required in Enterprise mode) |
| user_token | string JWT user token (required in Enterprise mode) |
| tenant_id | string Tenant identifier (required in Enterprise mode, defaults to "default" in Community) |
| user_id | string Optional end-user identifier. Honored only for internal-service callers, which may assert the end user; it attributes the decision's audit row |
| user_role | string Optional end-user role (e.g., "admin", "analyst"). Honored only for internal-service callers; recorded on the decision's audit row |
| connector_type required | string MCP connector/server type (e.g., "postgres", "snowflake", "salesforce") |
| tool | string Optional tool identifier being invoked, distinct from connector_type/server (#2904). Feeds capability-scoped policy evaluation when set. |
| statement required | string The SQL query or command to validate against policies |
object Optional query parameters. Values are individually scanned for SQLi, PII, and compliance violations by the static policy engine. String values are scanned directly; nested objects/arrays are JSON-serialized before scanning; numeric values are converted to strings for PII/compliance detection. Boolean values are skipped. | |
| operation | string Default: "execute" Enum: "query" "execute" Operation type. Under the read-only posture it classifies the call as a read or a write ( |
| content_type | string Declared content type of |
{- "client_id": "analytics-app",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "tenant_id": "tenant-123",
- "connector_type": "postgres",
- "statement": "SELECT name, email FROM users WHERE id = $1",
- "parameters": {
- "1": "usr-001"
}, - "operation": "query"
}{- "allowed": true,
- "policies_evaluated": 12
}Validate MCP response data against configured policies without having executed the query through AxonFlow.
This endpoint enables external orchestrators to apply AxonFlow's output-side policy enforcement (PII redaction, exfiltration limits, SQLi response scanning) to data they fetched from MCP connectors independently.
The following checks are applied in order:
If PII is detected, the response includes redacted_data with masked values.
If exfiltration limits are exceeded, the response returns allowed: false.
response_data): Full policy evaluation including exfiltration checksmessage): SQLi scanning and PII checks only (no exfiltration limits)All check-output evaluations are logged to the mcp_query_audits table with
operation: "check-output" for compliance tracking.
| X-Axonflow-Client | string Example: mcp-proxy/0.3.1 Optional client-version telemetry header ( |
| client_id | string Client identifier (required in Enterprise mode) |
| user_token | string JWT user token (required in Enterprise mode) |
| tenant_id | string Tenant identifier (required in Enterprise mode, defaults to "default" in Community) |
| user_id | string Optional user identifier |
| connector_type required | string MCP connector/server type (e.g., "postgres", "snowflake", "salesforce") |
| tool | string Optional tool identifier whose output is being validated, distinct from connector_type/server (#2904/#2955). Feeds capability-scoped response evaluation when set (a text-document tool's output skips execution-class detectors); omitted → full (fail-closed) evaluation, no fallback from connector_type. |
required | Array of objects Query-style response rows to validate |
| message | string Execute-style response message (e.g., "5 rows affected") |
object Connector metadata for SQLi response scanning (e.g., query echo, database name) | |
| row_count | integer Total number of rows returned (used for exfiltration limit checks) |
{- "client_id": "analytics-app",
- "user_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "tenant_id": "tenant-123",
- "connector_type": "postgres",
- "tool": "query",
- "response_data": [
- {
- "id": 1,
- "name": "Alice Johnson",
- "department": "Engineering"
}
], - "row_count": 1
}{- "allowed": true,
- "policies_evaluated": 8,
- "engine": "anchored",
- "subject_type": "User",
- "policy_bundle": "sha256:4b8f2a7c9e1d3f5a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a",
- "policy_packs": [
- "rbi@sha256:7d1e9a3c5b7f9d1e3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f"
]
}Unauthenticated liveness probe.
⚠️ healthy_count + unhealthy_count do not sum to
total_connectors, by design (platform/agent/mcp_handler.go:3377-3405).
Because this route is anonymous, the live health checks it performs
are limited to the deployment-shared (operator-configured,
wildcard-tenancy *) connectors, while total_connectors keeps its
pre-existing deployment-wide meaning so operator dashboards do not
silently change scale. A deployment consisting only of tenant-owned
connectors therefore reports healthy: true with zero counts even if
every backend is down.
Use the authenticated GET /mcp/connectors for per-tenant connector
health.
(Behaviour change in #3067: this route previously opened a live connection to every tenant's backend on every anonymous GET — cross-tenant credential use plus a free amplification lever.)
{- "healthy": true,
- "total_connectors": 3,
- "healthy_count": 1,
- "unhealthy_count": 0,
- "timestamp": "2026-07-28T10:30:00Z"
}Invalidates and refreshes cached connector instances. Use this after configuration changes or deployments.
The scope depends on which credential you present
(platform/agent/connector_refresh_api.go:184-216):
| Credential | Scope | message |
tenant_id |
|---|---|---|---|
| Basic auth (tenant) | the authenticated tenant's connectors only | Tenant connector caches refreshed |
present |
| Internal-service | every tenant's connectors | All connector caches refreshed |
absent |
Performance impact: the next request for each evicted connector incurs factory-creation overhead.
Authentication. This route is wrapped in apiAuthMiddleware
(platform/agent/connector_refresh_api.go:123) and the tenancy comes
from the authenticated identity — a caller can no longer evict another
tenant's pool. Supply either:
Authorization: Basic base64(clientId:clientSecret) — the tenant
lane (no credentials are required when
DEPLOYMENT_MODE=community); orX-Internal-Service-ID + X-Internal-Service-Token — the
operator lane, which is the only way to trigger a deployment-wide
eviction.(Historical note: these four routes were registered with no auth middleware at all — #2883. They are authenticated as of #3067; the pre-#3067 "deploy behind network-level controls" advisory no longer applies.)
stats.cached_connectors is structurally 0 on this route: the
refresh empties the scope immediately before the count is taken. That
is the success signal, not a failure.
{- "success": true,
- "message": "Tenant connector caches refreshed",
- "scope": "all",
- "tenant_id": "acme-corp",
- "duration": "12.345ms",
- "stats": {
- "cached_connectors": 0
}
}Invalidates and refreshes all cached connector instances for a tenant. Use this after updating a tenant's connector configuration.
{tenant_id} is not a selector. It is validated against the
identity apiAuthMiddleware resolved and a mismatch is 403
(platform/agent/connector_refresh_api.go:152-156); the tenancy that
is actually refreshed always comes from the credential.
X-Tenant-ID you send (platform/agent/authenticator.go:127-130),
so an operator targets a named tenant by sending that header and
the matching path segment. Naming a tenant in the path with no
X-Tenant-ID is 403, because the identity then falls back to the
synthetic orchestrator-internal client id.stats.cached_connectors is structurally 0 on this route — the
refresh empties the tenant's scope immediately before the count is
taken.
| tenant_id required | string Example: acme-corp The tenant whose connectors should be refreshed. Must equal the authenticated tenant — see the description. |
{- "success": true,
- "message": "Tenant connector caches refreshed",
- "scope": "tenant",
- "tenant_id": "acme-corp",
- "duration": "2.456ms",
- "stats": {
- "cached_connectors": 0
}
}Invalidates and refreshes a specific connector instance for a tenant. Use this after updating a single connector's credentials or configuration.
{tenant_id} is not a selector — same contract as
POST /api/v1/connectors/refresh/{tenant_id}: the segment is
validated against the authenticated identity and a mismatch is 403
(platform/agent/connector_refresh_api.go:152-156).
Unlike the two broader refresh routes, stats.cached_connectors here
is informative: it is the caller's remaining cached-connector
count after this one connector was evicted
(platform/agent/connector_refresh_api.go:314,404-408).
| tenant_id required | string Example: acme-corp The tenant that owns the connector. Must equal the authenticated tenant — see the description. |
| connector_name required | string Example: customer-db The connector name to refresh |
{- "success": true,
- "message": "Connector cache refreshed",
- "scope": "connector",
- "tenant_id": "acme-corp",
- "connector": "customer-db",
- "duration": "0.789ms",
- "stats": {
- "cached_connectors": 1
}
}Returns the caller's cached-connector count. The response is
tenant-scoped (platform/agent/connector_refresh_api.go:350-355):
cached_connectors is the authenticated tenant's own count, not the
deployment's.
The cache-health counters are operator telemetry. hits,
misses, evictions, factory_creations, factory_failures,
connection_errors, hit_rate_percent, last_eviction and
last_factory_create are deployment-wide figures. They appear here
only inside a deployment object, and only for the
internal-service credential
(platform/agent/connector_refresh_api.go:356-369). A tenant caller
must not read their absence as zero — it has no cache-health data on
this surface.
Un-scoped equivalents for five of them are always available to
operators on /prometheus as
axonflow_connector_cache_stats{stat="cached_connectors"|"hits"|"misses"|"evictions"|"hit_rate"},
refreshed on every call to this endpoint
(platform/agent/connector_refresh_api.go:338-342).
factory_creations, factory_failures, connection_errors,
last_eviction and last_factory_create have no Prometheus
series — the deployment block is their only surface.
Authentication. Wrapped in apiAuthMiddleware
(platform/agent/connector_refresh_api.go:132). This route takes no
{tenant_id} path segment, so it never returns 403.
(Historical note: this route was registered with no auth middleware
at all and served the deployment-wide counters to anonymous callers —
#2883, where the evictions delta was an existence oracle for
(tenant, connector) pairs. Authenticated and scoped as of #3067.)
{- "cached_connectors": 15,
- "registry_enabled": true,
- "tenant_id": "acme-corp",
- "timestamp": "2026-07-28T14:25:00.123456Z"
}Returns real-time performance metrics including:
{- "agent_metrics": {
- "uptime_seconds": 0,
- "total_requests": 0,
- "success_requests": 0,
- "failed_requests": 0,
- "blocked_requests": 0,
- "success_rate": 0,
- "rps": 0,
- "error_rate_per_sec": 0,
- "p50_ms": 0,
- "p95_ms": 0,
- "p99_ms": 0,
- "avg_latency_ms": 0,
- "auth_p99_ms": 0,
- "static_policy_eval_p99_ms": 0,
- "network_p99_ms": 0
}, - "health": {
- "status": "healthy",
- "healthy": true,
- "consecutive_errors": 0,
- "up": 0
}, - "request_types": {
- "property1": {
- "total_requests": 0,
- "success_requests": 0,
- "p99_ms": 0
}, - "property2": {
- "total_requests": 0,
- "success_requests": 0,
- "p99_ms": 0
}
}, - "connectors": {
- "property1": {
- "total_requests": 0,
- "success_rate": 0,
- "p99_ms": 0
}, - "property2": {
- "total_requests": 0,
- "success_rate": 0,
- "p99_ms": 0
}
}, - "timestamp": "2019-08-24T14:15:22Z"
}Human-in-the-Loop decision queue (EU AI Act Article 14). Route high-risk AI decisions for human review before execution.
Route a high-risk AI decision for human review. EU AI Act Article 14 requires human oversight for high-risk AI systems.
Requires both X-Org-ID and X-Tenant-ID headers (stamped by the
auth middleware) — 400 when either is missing.
Enterprise only — community builds expose only
GET /api/v1/hitl/status.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| Idempotency-Key | string [ 1 .. 256 ] characters ^[A-Za-z0-9_.:\-/]+$ Example: n8n-exec-abc123-node-Approve Optional per-request dedup token. When supplied on POST /api/v1/mcp/check-input, POST /api/v1/audit/tool-call, or POST /api/v1/hitl/queue, the platform caches the original response for 24h and returns it byte-for-byte on subsequent requests carrying the same key + same authenticated tenant + same endpoint. Format: 1-256 chars, Cache rules: 2xx + 4xx responses are cached; 5xx is NOT cached so the
caller's retry can hit a fresh attempt. A cache hit returns the original
response plus an Cross-tenant collisions are impossible: tenant_id participates in the primary key + an RLS policy on the storage table. Two tenants using the same key value get distinct rows. |
| client_id required | string |
| user_id | string |
| original_query required | string The query/action awaiting human review |
| request_type required | string Request classification (e.g. |
object | |
| triggered_policy_id required | string |
| triggered_policy_name required | string |
| trigger_reason required | string |
| severity | string Enum: "low" "medium" "high" "critical" |
| eu_ai_act_article | string |
| compliance_framework | string |
| risk_classification | string |
| expires_in_seconds | integer TTL before the request auto-expires |
| notify_url | string <uri> Optional outbound webhook URL fired asynchronously after the request
transitions to a terminal state (approved / rejected / overridden /
expired). Must use |
{- "client_id": "string",
- "user_id": "string",
- "original_query": "string",
- "request_type": "string",
- "request_context": { },
- "triggered_policy_id": "string",
- "triggered_policy_name": "string",
- "trigger_reason": "string",
- "severity": "low",
- "eu_ai_act_article": "string",
- "compliance_framework": "string",
- "risk_classification": "string",
- "expires_in_seconds": 0,
}{- "success": true,
- "data": {
- "id": 0,
- "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
- "org_id": "string",
- "tenant_id": "string",
- "client_id": "string",
- "user_id": "string",
- "original_query": "string",
- "request_type": "string",
- "request_context": { },
- "triggered_policy_id": "string",
- "triggered_policy_name": "string",
- "trigger_reason": "string",
- "severity": "string",
- "eu_ai_act_article": "string",
- "compliance_framework": "string",
- "risk_classification": "string",
- "status": "pending",
- "reviewer_id": "string",
- "reviewer_email": "string",
- "reviewer_role": "string",
- "review_comment": "string",
- "reviewed_at": "2019-08-24T14:15:22Z",
- "override_justification": "string",
- "override_authorized_by": "string",
- "notify_url": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Retrieve approval requests, filtered and paginated. Tenant scoping comes from the authenticated request context (RLS), not a query parameter.
| status | string Example: status=pending Comma-separated status filter (e.g. |
| severity | string Example: severity=high,critical Comma-separated severity filter |
| policy_id | string |
| client_id | string |
| user_id | string |
| request_type | string Example: request_type=wcp_step_gate Narrow the listing to one Default behaviour without it (#3408): the listing is the
ACTIONABLE queue and EXCLUDES
|
| limit | integer Default: 50 |
| offset | integer Default: 0 |
| order_by | string |
| order_dir | string Enum: "asc" "desc" |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": [
- {
- "id": 0,
- "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
- "org_id": "string",
- "tenant_id": "string",
- "client_id": "string",
- "user_id": "string",
- "original_query": "string",
- "request_type": "string",
- "request_context": { },
- "triggered_policy_id": "string",
- "triggered_policy_name": "string",
- "trigger_reason": "string",
- "severity": "string",
- "eu_ai_act_article": "string",
- "compliance_framework": "string",
- "risk_classification": "string",
- "status": "pending",
- "reviewer_id": "string",
- "reviewer_email": "string",
- "reviewer_role": "string",
- "review_comment": "string",
- "reviewed_at": "2019-08-24T14:15:22Z",
- "override_justification": "string",
- "override_authorized_by": "string",
- "notify_url": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "meta": {
- "total": 0,
- "limit": 0,
- "offset": 0
}
}| id required | string <uuid> The request UUID ( |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "id": 0,
- "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
- "org_id": "string",
- "tenant_id": "string",
- "client_id": "string",
- "user_id": "string",
- "original_query": "string",
- "request_type": "string",
- "request_context": { },
- "triggered_policy_id": "string",
- "triggered_policy_name": "string",
- "trigger_reason": "string",
- "severity": "string",
- "eu_ai_act_article": "string",
- "compliance_framework": "string",
- "risk_classification": "string",
- "status": "pending",
- "reviewer_id": "string",
- "reviewer_email": "string",
- "reviewer_role": "string",
- "review_comment": "string",
- "reviewed_at": "2019-08-24T14:15:22Z",
- "override_justification": "string",
- "override_authorized_by": "string",
- "notify_url": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Approve a pending request to allow AI execution
| id required | string <uuid> |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| reviewer_id | string |
| reviewer_email | string |
| reviewer_role | string |
| comment | string |
{- "reviewer_id": "string",
- "reviewer_email": "string",
- "reviewer_role": "string",
- "comment": "string"
}{- "success": true,
- "data": {
- "status": "approved"
}
}Reject a pending request to block AI execution
| id required | string <uuid> |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| reviewer_id | string |
| reviewer_email | string |
| reviewer_role | string |
| comment | string |
{- "reviewer_id": "string",
- "reviewer_email": "string",
- "reviewer_role": "string",
- "comment": "string"
}{- "success": true,
- "data": {
- "status": "rejected"
}
}Record an authorized override of a pending request — the caller takes responsibility for letting the action proceed outside the normal approve/reject flow. Requires a justification for the audit trail.
| id required | string <uuid> |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| justification | string Required by the service — missing justification returns 400 |
| authorized_by_id | string |
| authorized_by_email | string |
| authorized_by_role | string |
{- "justification": "string",
- "authorized_by_id": "string",
- "authorized_by_email": "string",
- "authorized_by_role": "string"
}{- "success": true,
- "data": {
- "status": "overridden"
}
}Immutable audit-trail entries for a request (create/approve/reject/override/expire actions).
| id required | string <uuid> |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": [
- {
- "id": 0,
- "request_id": "266ea41d-adf5-480b-af50-15b940c2b846",
- "org_id": "string",
- "tenant_id": "string",
- "action": "string",
- "actor_id": "string",
- "actor_email": "string",
- "actor_role": "string",
- "actor_ip": "string",
- "comment": "string",
- "justification": "string",
- "previous_status": "string",
- "new_status": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
]
}Dashboard summary of the pending queue for the caller's org.
Requires the X-Org-ID header (400 when missing).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "total_pending": 0,
- "high_priority": 0,
- "critical_priority": 0,
- "oldest_pending_hours": 0
}
}Reports whether HITL is enabled and which features are available.
This is the only HITL endpoint present in community builds
(returns enabled: false, mode: "community"); Enterprise builds
return enabled: true, mode: "enterprise" plus a feature map
(queue, approve_reject, override, expiration, audit_history,
pending_summary, notify_url, idempotency_key).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "enabled": true,
- "mode": "community",
- "features": {
- "property1": true,
- "property2": true
}
}Sweeps pending requests past their expires_at into the expired
state and returns the count. Intended for schedulers/ops automation;
the platform also expires lazily.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "expired_count": 0
}
}EU AI Act compliance module (conformity assessments, accuracy
monitoring, evidence exports). Served by the orchestrator and PROXIED
through the agent (/api/v1/euaiact/*) per the single-entry-point
architecture — clients call the agent, never the orchestrator.
Canonical operation schemas live in orchestrator-api.yaml. Enterprise only.
Agent-proxied. Canonical contract in orchestrator-api.yaml GET /api/v1/euaiact/export.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml POST /api/v1/euaiact/export.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| export_id required | string Export request identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| export_id required | string Export request identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml GET /api/v1/euaiact/conformity.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml POST /api/v1/euaiact/conformity.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| assessment_id required | string Conformity assessment identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| assessment_id required | string Conformity assessment identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| assessment_id required | string Conformity assessment identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| assessment_id required | string Conformity assessment identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| assessment_id required | string Conformity assessment identifier |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml GET /api/v1/euaiact/accuracy.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Agent-proxied. Canonical contract in orchestrator-api.yaml.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
Emergency circuit breaker for AI operations (EU AI Act Article 14). Instantly halt AI operations with two-person deactivation requirement.
Immediately halt matching AI operations for the organization
(EU AI Act Article 14). Also exposed as the alias
POST /api/v1/emergency-stop.
Identity comes from headers stamped by the auth middleware:
X-Org-ID (falls back to X-Tenant-ID) selects the org;
X-User-ID is required for the Article 14 audit trail
(400 when missing). X-User-Email is recorded when present.
Enterprise only — community builds register no circuit-breaker routes (404).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| scope | string Default: "global" Enum: "global" "tenant" "client" "policy" Blast radius of the stop |
| scope_id | string Required for non-global scopes (tenant/client/policy ID) |
| reason | string Default: "manual" Enum: "manual" "policy_violation" "risk_level" "error_rate" |
| comment | string Free-text audit-trail comment |
| duration_minutes | integer Auto-expire after N minutes; 0 or omitted = indefinite |
{- "scope": "global",
- "scope_id": "string",
- "reason": "manual",
- "comment": "string",
- "duration_minutes": 0
}{- "success": true,
- "data": {
- "circuit_id": "string",
- "state": "string",
- "scope": "string",
- "scope_id": "string",
- "tripped_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z",
- "message": "string"
}
}Release an emergency stop and resume normal operations. Also exposed
as the alias POST /api/v1/emergency-stop/release. Requires
X-Org-ID (or X-Tenant-ID) and X-User-ID headers (400 when
missing). Enterprise only.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| scope | string Default: "global" Enum: "global" "tenant" "client" "policy" |
| scope_id | string |
| comment | string Free-text audit-trail comment |
{- "scope": "global",
- "scope_id": "string",
- "comment": "string"
}{- "success": true,
- "data": {
- "scope": "string",
- "scope_id": "string",
- "state": "closed",
- "message": "string"
}
}Evaluates the caller's scope hierarchy (global → tenant → client → policy) and reports whether an open circuit would block the request. Read-only; does not mutate circuit state. Enterprise only.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| tenant_id | string |
| client_id | string |
| policy_id | string |
{- "tenant_id": "string",
- "client_id": "string",
- "policy_id": "string"
}{- "success": true,
- "data": {
- "allowed": true,
- "circuit_id": "string",
- "scope": "string",
- "scope_id": "string",
- "reason": "string",
- "tripped_by": "string",
- "tripped_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z",
- "comment": "string"
}
}Clearer Article 14 naming for POST /api/v1/circuit-breaker/trip —
identical handler, request body, and responses. Enterprise only.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| scope | string Default: "global" Enum: "global" "tenant" "client" "policy" Blast radius of the stop |
| scope_id | string Required for non-global scopes (tenant/client/policy ID) |
| reason | string Default: "manual" Enum: "manual" "policy_violation" "risk_level" "error_rate" |
| comment | string Free-text audit-trail comment |
| duration_minutes | integer Auto-expire after N minutes; 0 or omitted = indefinite |
{- "scope": "global",
- "scope_id": "string",
- "reason": "manual",
- "comment": "string",
- "duration_minutes": 0
}{- "success": false,
- "error": "Invalid request body"
}Clearer Article 14 naming for POST /api/v1/circuit-breaker/reset —
identical handler, request body, and responses. Enterprise only.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| scope | string Default: "global" Enum: "global" "tenant" "client" "policy" |
| scope_id | string |
| comment | string Free-text audit-trail comment |
{- "scope": "global",
- "scope_id": "string",
- "comment": "string"
}{- "success": false,
- "error": "Invalid request body"
}Returns all active (open) circuits for the organization. Uses X-Org-ID header for org identification.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "active_circuits": [
- {
- "id": "string",
- "scope": "global",
- "scope_id": "string",
- "org_id": "string",
- "state": "closed",
- "trip_reason": "manual",
- "tripped_by": "string",
- "tripped_by_email": "string",
- "trip_comment": "string",
- "tripped_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z",
- "reset_by": "string",
- "reset_at": "2019-08-24T14:15:22Z",
- "error_count": 0,
- "violation_count": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0,
- "emergency_stop_active": true
}
}Returns circuit breaker trip/reset history for audit trail. Ordered by creation time descending.
| limit | integer [ 1 .. 100 ] Default: 50 |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "history": [
- {
- "id": "string",
- "scope": "global",
- "scope_id": "string",
- "org_id": "string",
- "state": "closed",
- "trip_reason": "manual",
- "tripped_by": "string",
- "tripped_by_email": "string",
- "trip_comment": "string",
- "tripped_at": "2019-08-24T14:15:22Z",
- "expires_at": "2019-08-24T14:15:22Z",
- "reset_by": "string",
- "reset_at": "2019-08-24T14:15:22Z",
- "error_count": 0,
- "violation_count": 0,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}
}Returns effective circuit breaker configuration. If tenant_id is provided, returns tenant-specific overrides merged with global defaults. Otherwise returns global defaults.
| tenant_id | string Optional tenant ID for tenant-specific config |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "source": "global",
- "error_threshold": 0,
- "violation_threshold": 0,
- "window_seconds": 0,
- "default_timeout_seconds": 0,
- "max_timeout_seconds": 0,
- "enable_auto_recovery": true,
- "tenant_id": "string",
- "overrides": { }
}
}Creates or updates per-tenant circuit breaker threshold overrides. Null fields fall back to global defaults.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| tenant_id required | string |
| error_threshold | integer or null |
| violation_threshold | integer or null |
| window_seconds | integer or null |
| default_timeout_seconds | integer or null |
| max_timeout_seconds | integer or null |
| enable_auto_recovery | boolean or null |
{- "tenant_id": "string",
- "error_threshold": 0,
- "violation_threshold": 0,
- "window_seconds": 0,
- "default_timeout_seconds": 0,
- "max_timeout_seconds": 0,
- "enable_auto_recovery": true
}{- "success": false,
- "error": "Invalid request body"
}Returns all circuit breaker notification configs for the organization.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": true,
- "data": {
- "notifications": [
- {
- "id": "string",
- "org_id": "string",
- "tenant_id": "string",
- "type": "webhook",
- "url": "string",
- "secret": "string",
- "active": true,
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}
}Create a notification channel for circuit breaker auto-trip events. Supports webhook (HMAC-signed), Slack (Block Kit), and PagerDuty (Events API v2).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| type required | string Enum: "webhook" "slack" "pagerduty" |
| url required | string Webhook URL, Slack incoming webhook URL, or PagerDuty override URL |
| secret | string HMAC secret for webhooks, or PagerDuty routing key |
| tenant_id | string Optional tenant filter for notifications |
| active | boolean Default: true |
{- "type": "webhook",
- "url": "string",
- "secret": "string",
- "tenant_id": "string",
- "active": true
}{- "success": false,
- "error": "Invalid request body"
}| id required | string |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| type | string Enum: "webhook" "slack" "pagerduty" |
| url | string |
| secret | string |
| tenant_id | string |
| active | boolean |
{- "type": "webhook",
- "url": "string",
- "secret": "string",
- "tenant_id": "string",
- "active": true
}| id required | string |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
OpenAI-compatible gateway endpoint (Issue #2351). Accepts standard
OpenAI Chat Completions requests, runs AxonFlow policy checks, forwards
to the upstream provider, records audit, and returns an OpenAI-compatible
response. Customers change only baseURL in their OpenAI SDK setup.
Accepts a standard OpenAI Chat Completions request, decides it with the ADR-065 decision plane (PRD v11 §1.1; #4092) - the shared policy engine's detectors are its input - forwards an allowed request to the upstream provider, records audit (tokens, cost, latency, policy decision), and returns an OpenAI-compatible response.
The route carries no per-user identity, so every request is decided
for its client credential (Client). OpenAI's user request member
is NOT honoured as an identity: it is free text the caller chooses,
and a principal is never taken from it.
The caller passes their upstream provider API key via the
X-Provider-Key header. AxonFlow auth (Basic Auth or community
mode) is handled by the same apiAuthMiddleware as all other
agent endpoints.
Streaming (stream: true) is not supported in this release and
returns HTTP 400 with a clear error.
| X-Provider-Key required | string Upstream LLM provider API key (e.g. OpenAI API key) |
| traceparent | string W3C traceparent header for trace correlation |
| model required | string ID of the model to use (e.g. gpt-4o, gpt-4o-mini). |
required | Array of objects non-empty |
| temperature | number [ 0 .. 2 ] |
| top_p | number |
| max_tokens | integer |
| max_completion_tokens | integer |
| stream | boolean Must be false or omitted. Streaming is not supported in this release; setting stream=true returns HTTP 400. |
| stop | any Up to 4 stop sequences. |
| presence_penalty | number |
| frequency_penalty | number |
| user | string |
| response_format | object |
| seed | integer |
| tools | Array of objects |
| tool_choice | any Tool choice configuration. |
{- "model": "gpt-4o",
- "messages": [
- {
- "role": "user",
- "content": "What is 2+2?"
}
], - "temperature": 0.7,
- "max_tokens": 100
}{- "id": "chatcmpl-abc123",
- "object": "chat.completion",
- "created": 0,
- "model": "string",
- "choices": [
- {
- "index": 0,
- "message": {
- "role": "string",
- "content": "string",
- "tool_calls": [
- { }
]
}, - "finish_reason": "stop"
}
], - "usage": {
- "prompt_tokens": 0,
- "completion_tokens": 0,
- "total_tokens": 0
}, - "system_fingerprint": "string"
}System policy management (ADR-019), served at /api/v1/system-policies.
Pattern-based enforcement rules for SQL injection detection, PII
detection and similar checks, resolved across the system, organization
and tenant tiers.
v11: writes to this family are refused by the legacy policy freeze.
migrations/core/172 makes static_policies read-only to the
application roles, and create, update, delete and the enabled toggle
write that table. On a deployment whose connection is an application
role (the agent's default) those writes are
refused with 409 LEGACY_POLICY_WRITE_FROZEN, the answer the
orchestrator gives for its own policy writes (see policy-api.yaml).
Its error.code is that string, where the other errors on these routes
carry the numeric status. Author policy through the typed authoring
route, /api/v1/typed-policies in orchestrator-api.yaml. Reads, the
pattern test, and the per-policy overrides - which are stored in their
own table - are unaffected. A deployment whose connection may still
write the table (the database owner; a property of the connection, not
of AXONFLOW_DB_USE_APP_ROLE alone) is not bound by the revoke.
Portal-facing alias of GET /api/v1/system-policies/overrides
(deprecated spelling: GET /api/v1/static-policies/overrides):
identical handler, parameters, and response.
This route itself is not deprecated and emits no Deprecation header.
| include_expired | boolean Default: false Include expired overrides in results |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "overrides": [
- {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}Returns static policies with three-tier hierarchy resolution:
v2.0.0 Categories (semantic naming):
security-sqli - SQL injection detection (was: sql_injection)security-admin - Admin access protection (was: admin_access)pii-global - Global PII patterns (was: pii_detection)pii-us - US-specific PII (SSN, etc.)pii-eu - EU-specific PII (GDPR)pii-india - India-specific PII (Aadhaar, PAN)custom - Tenant-created policiesLegacy category names are still accepted and automatically mapped.
Part of ADR-019: Unified Policy Management System.
| page | integer >= 1 Default: 1 Page number (1-indexed) |
| page_size | integer [ 1 .. 100 ] Default: 20 Number of policies per page |
| category | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" "sql_injection" "pii_detection" "dangerous_queries" "admin_access" Filter by policy category. New categories use semantic naming. Legacy names (sql_injection, pii_detection, etc.) are still accepted. |
| tier | string Enum: "system" "organization" "tenant" Filter by policy tier |
| severity | string Enum: "critical" "high" "medium" "low" Filter by severity level |
| enabled | boolean Filter by enabled status |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "policies": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "page": 0,
- "page_size": 0,
- "total": 0,
- "total_pages": 0
}
}Creates a new static policy for the tenant.
Tier restrictions:
system tier: Cannot be created via API (managed by AxonFlow)organization tier: Requires Enterprise licensetenant tier: Default, limited to 20 policies in Community modePart of ADR-019: Unified Policy Management System.
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read,
whatever it contains, naming the typed authoring route. An
owner-role deployment still creates.
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
| name required | string Display name of the policy |
| description | string Detailed policy description |
| category required | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" Policy category |
| tier | string Default: "tenant" Enum: "organization" "tenant" Policy tier (system not allowed via API). Default is tenant. |
| pattern required | string Regex pattern for detection |
| action required | string Enum: "block" "require_approval" "redact" "warn" "log" Action to take when pattern matches |
| severity | string Enum: "critical" "high" "medium" "low" Severity level |
| priority | integer Default: 100 Priority order (lower = higher priority) |
| enabled | boolean Default: true Whether the policy is active |
| tags | Array of strings Tags for categorization |
{- "name": "Custom Employee ID Detection",
- "description": "Detects internal employee ID format",
- "category": "custom",
- "tier": "tenant",
- "pattern": "EMP-[0-9]{6}",
- "action": "warn",
- "severity": "medium",
- "enabled": true,
- "tags": [
- "custom",
- "pii",
- "employee"
]
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Returns all effective policies for a tenant with overrides applied.
This endpoint resolves the three-tier hierarchy:
Used by the Customer Portal for the unified policy view.
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "static": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "tenant_id": "string",
- "organization_id": "string",
- "computed_at": "2019-08-24T14:15:22Z"
}Tests a regex pattern against input strings without creating a policy.
Useful for validating patterns before creating policies. Has a 5-second timeout to prevent ReDoS attacks.
| pattern required | string Regex pattern to test |
| input | string Single input string to test (for backward compatibility) |
| inputs | Array of strings Multiple input strings to test |
{- "pattern": "\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b",
- "input": "My SSN is 123-45-6789"
}{- "valid": true,
- "results": [
- {
- "input": "string",
- "matched": true,
- "matches": [
- "string"
]
}
], - "error": "string"
}Lists all policy overrides for a tenant.
Overrides allow Enterprise customers to modify system policy behavior (action, enabled status) without changing the underlying pattern.
| include_expired | boolean Default: false Include expired overrides in results |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "overrides": [
- {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}Returns a single static policy by its UUID.
The policy ID is the id field from the static_policies table,
not the policy_id (human-readable identifier like 'sql_injection_union').
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Updates an existing static policy.
Restrictions:
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read and
before the policy is looked up, whatever it contains, naming the
typed authoring route. An owner-role deployment still updates.
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
| name | string Display name of the policy |
| description | string Detailed policy description |
| pattern | string Regex pattern (only for non-system policies) |
| action | string Enum: "block" "require_approval" "redact" "warn" "log" |
| severity | string Enum: "critical" "high" "medium" "low" |
| priority | integer |
| enabled | boolean |
| category | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" Policy category. Updates can re-categorise non-system policies; system policies stay pinned to their seeded category. |
| tags | Array of strings |
{- "name": "string",
- "description": "string",
- "pattern": "string",
- "action": "block",
- "severity": "critical",
- "priority": 0,
- "enabled": true,
- "category": "security-sqli",
- "tags": [
- "string"
]
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Soft-deletes a static policy (sets deleted_at timestamp).
Restrictions:
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
{- "success": false,
- "error": "string"
}Toggles the enabled status of a policy.
Restrictions:
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read and
before the policy is looked up, whatever it contains, naming the
typed authoring route. An owner-role deployment still toggles.
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
| enabled required | boolean New enabled status |
{- "enabled": true
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Returns the version history for a policy.
Edition limits:
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "policy_id": "string",
- "versions": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "string",
- "version": 0,
- "snapshot": { },
- "change_type": "created",
- "change_summary": "string",
- "changed_by": "string",
- "changed_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}Returns the active override for a single policy (404 when none).
{id} may be the policy UUID or the human-readable slug - slugs
are resolved to the canonical UUID before lookup.
| id required | string |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}Retired in v11 (PRD v11 §1.5): a system control is enabled, disabled
or re-actioned in the organization's typed document, in its
system_controls section, through /api/v1/typed-policies. This
route writes nothing and does not read a body: it answers 409 LEGACY_POLICY_WRITE_FROZEN to every authenticated caller, in every
edition. The ADR-044 session override writes answer the same refusal
since v11.0.0 (#4252); the override reads are unaffected.
| id required | string <uuid> Static policy UUID to override |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
| action_override | string Enum: "block" "require_approval" "redact" "warn" "log" Override the policy action |
| enabled_override | boolean Override the enabled status |
| override_reason required | string Required explanation for audit trail |
| expires_at | string <date-time> Optional expiration for the override |
{- "enabled_override": false,
- "override_reason": "False positive rate too high for this tenant",
- "expires_at": "2025-06-01T00:00:00Z"
}{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}Retired in v11 (PRD v11 §1.5): a system control is enabled, disabled
or re-actioned in the organization's typed document, in its
system_controls section, through /api/v1/typed-policies. This
route writes nothing and does not read a body: it answers 409 LEGACY_POLICY_WRITE_FROZEN to every authenticated caller, in every
edition. The ADR-044 session override writes answer the same refusal
since v11.0.0 (#4252); the override reads are unaffected.
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}DEPRECATED spelling of System Policies, served at
/api/v1/static-policies. Every operation is the same handler as its
/api/v1/system-policies counterpart and returns the same status code
and body.
In v11 BOTH spellings are the deprecated export surface (PRD §1.11):
reads are served, and every response carries
Link: </api/v1/typed-policies>; rel="successor-version" and
X-AxonFlow-Removed-In: v11.1, plus an RFC 9745
Deprecation: @<epoch> from the v11.0.0 tag. v11.1 removes both
spellings; there is no Sunset header until that release has a date.
The v11 write refusal described under System Policies applies to these
paths identically.
Returns static policies with three-tier hierarchy resolution:
v2.0.0 Categories (semantic naming):
security-sqli - SQL injection detection (was: sql_injection)security-admin - Admin access protection (was: admin_access)pii-global - Global PII patterns (was: pii_detection)pii-us - US-specific PII (SSN, etc.)pii-eu - EU-specific PII (GDPR)pii-india - India-specific PII (Aadhaar, PAN)custom - Tenant-created policiesLegacy category names are still accepted and automatically mapped.
Part of ADR-019: Unified Policy Management System.
| page | integer >= 1 Default: 1 Page number (1-indexed) |
| page_size | integer [ 1 .. 100 ] Default: 20 Number of policies per page |
| category | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" "sql_injection" "pii_detection" "dangerous_queries" "admin_access" Filter by policy category. New categories use semantic naming. Legacy names (sql_injection, pii_detection, etc.) are still accepted. |
| tier | string Enum: "system" "organization" "tenant" Filter by policy tier |
| severity | string Enum: "critical" "high" "medium" "low" Filter by severity level |
| enabled | boolean Filter by enabled status |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "policies": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "page": 0,
- "page_size": 0,
- "total": 0,
- "total_pages": 0
}
}Creates a new static policy for the tenant.
Tier restrictions:
system tier: Cannot be created via API (managed by AxonFlow)organization tier: Requires Enterprise licensetenant tier: Default, limited to 20 policies in Community modePart of ADR-019: Unified Policy Management System.
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read,
whatever it contains, naming the typed authoring route. An
owner-role deployment still creates.
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
| name required | string Display name of the policy |
| description | string Detailed policy description |
| category required | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" Policy category |
| tier | string Default: "tenant" Enum: "organization" "tenant" Policy tier (system not allowed via API). Default is tenant. |
| pattern required | string Regex pattern for detection |
| action required | string Enum: "block" "require_approval" "redact" "warn" "log" Action to take when pattern matches |
| severity | string Enum: "critical" "high" "medium" "low" Severity level |
| priority | integer Default: 100 Priority order (lower = higher priority) |
| enabled | boolean Default: true Whether the policy is active |
| tags | Array of strings Tags for categorization |
{- "name": "Custom Employee ID Detection",
- "description": "Detects internal employee ID format",
- "category": "custom",
- "tier": "tenant",
- "pattern": "EMP-[0-9]{6}",
- "action": "warn",
- "severity": "medium",
- "enabled": true,
- "tags": [
- "custom",
- "pii",
- "employee"
]
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Returns all effective policies for a tenant with overrides applied.
This endpoint resolves the three-tier hierarchy:
Used by the Customer Portal for the unified policy view.
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "static": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "tenant_id": "string",
- "organization_id": "string",
- "computed_at": "2019-08-24T14:15:22Z"
}Tests a regex pattern against input strings without creating a policy.
Useful for validating patterns before creating policies. Has a 5-second timeout to prevent ReDoS attacks.
The pattern is compiled as written. The engine compiles a stored
pattern in the SQL-injection and destructive-command categories
case-insensitively, so for those categories prefix (?i) to test
what the engine matches.
| pattern required | string Regex pattern to test |
| input | string Single input string to test (for backward compatibility) |
| inputs | Array of strings Multiple input strings to test |
{- "pattern": "\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b",
- "input": "My SSN is 123-45-6789"
}{- "valid": true,
- "results": [
- {
- "input": "string",
- "matched": true,
- "matches": [
- "string"
]
}
], - "error": "string"
}Lists all policy overrides for a tenant.
Overrides allow Enterprise customers to modify system policy behavior (action, enabled status) without changing the underlying pattern.
| include_expired | boolean Default: false Include expired overrides in results |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "overrides": [
- {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}Returns a single static policy by its UUID.
The policy ID is the id field from the static_policies table,
not the policy_id (human-readable identifier like 'sql_injection_union').
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Updates an existing static policy.
Restrictions:
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read and
before the policy is looked up, whatever it contains, naming the
typed authoring route. An owner-role deployment still updates.
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
| name | string Display name of the policy |
| description | string Detailed policy description |
| pattern | string Regex pattern (only for non-system policies) |
| action | string Enum: "block" "require_approval" "redact" "warn" "log" |
| severity | string Enum: "critical" "high" "medium" "low" |
| priority | integer |
| enabled | boolean |
| category | string Enum: "security-sqli" "security-admin" "pii-global" "pii-us" "pii-eu" "pii-india" "custom" Policy category. Updates can re-categorise non-system policies; system policies stay pinned to their seeded category. |
| tags | Array of strings |
{- "name": "string",
- "description": "string",
- "pattern": "string",
- "action": "block",
- "severity": "critical",
- "priority": 0,
- "enabled": true,
- "category": "security-sqli",
- "tags": [
- "string"
]
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Soft-deletes a static policy (sets deleted_at timestamp).
Restrictions:
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
{- "success": false,
- "error": "string"
}Toggles the enabled status of a policy.
Restrictions:
v11: on a deployment whose database connection cannot write
static_policies (migrations/core/172), this route answers
409 LEGACY_POLICY_WRITE_FROZEN before the request body is read and
before the policy is looked up, whatever it contains, naming the
typed authoring route. An owner-role deployment still toggles.
| id required | string <uuid> Static policy UUID |
| X-User-ID | string User ID for audit trail |
| enabled required | boolean New enabled status |
{- "enabled": true
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "sys_sqli_union_select",
- "name": "UNION SELECT Detection",
- "description": "Detects SQL injection attempts using UNION SELECT",
- "category": "security-sqli",
- "tier": "system",
- "pattern": "(?i)\\bUNION\\s+(ALL\\s+)?SELECT\\b",
- "severity": "critical",
- "action": "block",
- "enabled": true,
- "priority": 1000,
- "version": 1,
- "has_override": true,
- "override": {
- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}, - "tenant_id": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Returns the version history for a policy.
Edition limits:
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
{- "policy_id": "string",
- "versions": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "policy_id": "string",
- "version": 0,
- "snapshot": { },
- "change_type": "created",
- "change_summary": "string",
- "changed_by": "string",
- "changed_at": "2019-08-24T14:15:22Z"
}
], - "count": 0
}Returns the active override for a single policy (404 when none).
{id} may be the policy UUID or the human-readable slug - slugs
are resolved to the canonical UUID before lookup.
| id required | string |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "id": "string",
- "policy_id": "string",
- "policy_type": "static",
- "tenant_id": "string",
- "enabled_override": true,
- "action_override": "block",
- "override_reason": "string",
- "expires_at": "2019-08-24T14:15:22Z",
- "created_by": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_by": "string",
- "updated_at": "2019-08-24T14:15:22Z"
}Retired in v11 (PRD v11 §1.5): a system control is enabled, disabled
or re-actioned in the organization's typed document, in its
system_controls section, through /api/v1/typed-policies. This
route writes nothing and does not read a body: it answers 409 LEGACY_POLICY_WRITE_FROZEN to every authenticated caller, in every
edition. The ADR-044 session override writes answer the same refusal
since v11.0.0 (#4252); the override reads are unaffected.
| id required | string <uuid> Static policy UUID to override |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
| action_override | string Enum: "block" "require_approval" "redact" "warn" "log" Override the policy action |
| enabled_override | boolean Override the enabled status |
| override_reason required | string Required explanation for audit trail |
| expires_at | string <date-time> Optional expiration for the override |
{- "enabled_override": false,
- "override_reason": "False positive rate too high for this tenant",
- "expires_at": "2025-06-01T00:00:00Z"
}{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}Retired in v11 (PRD v11 §1.5): a system control is enabled, disabled
or re-actioned in the organization's typed document, in its
system_controls section, through /api/v1/typed-policies. This
route writes nothing and does not read a body: it answers 409 LEGACY_POLICY_WRITE_FROZEN to every authenticated caller, in every
edition. The ADR-044 session override writes answer the same refusal
since v11.0.0 (#4252); the override reads are unaffected.
| id required | string <uuid> Static policy UUID |
| Authorization | string Basic auth credentials: Basic base64(clientId:clientSecret). Required in evaluation/enterprise mode. Optional in community mode (defaults to community tenant). |
| X-User-ID | string User ID for audit trail |
{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}Non-repudiation verification of the signed decision chain (#2722).
Read-only endpoints that re-verify per-record Ed25519 signatures and
the prev_hash linkage of a tenant's decision records, and publish the
current public signing key for offline verification. These are not
Policy Enforcement Points (no decision-engine call); they are org-scoped
via the authenticated request context and read under RLS, so a caller can
only verify chains/records belonging to its own organization. Available in
any deployment mode once a usage database is configured; signatures verify
only when an AXONFLOW_AUDIT_SIGNING_KEY is set (otherwise records are
hash-chained but reported as unsigned).
Re-verifies an entire decision chain: the prev_hash linkage across all
of its records (ordering / completeness) and every per-record Ed25519
signature (authorship). Read-only; never mutates the chain.
The chain is resolved by id only within the authenticated caller's
organization (RLS-scoped); the org is taken from the request
credentials, never from a path or query parameter. chainID is the
decision id (a UUID).
Inspect authorship_proven for the strong non-repudiation claim (every
record signed and all signatures + linkage verify). valid only means
no integrity violation was detected and can be true for a chain with
zero signed records, so do not gate non-repudiation on valid alone.
Authority (#2914). Authentication alone is not enough: this route
requires COMPLIANCE READ AUTHORITY over the organization. Present a
validated per-user token (X-User-Token, or Authorization: Bearer)
whose role is admin, owner or policy_admin, or call from an
internal service asserting X-Axonflow-Admin-Authority: true. A
Community-mode deployment is exempt: it has no authentication and
resolves every caller to one local operator. Anything else is 403.
Community SaaS (try.getaxonflow.com) refuses these routes, by
design. The hosted evaluation stack runs with authentication enabled,
so the Community-mode exemption above does not apply to it, and its
evaluator accounts hold no entitled role. Verification there is a 403.
Granting evaluators access would widen, on a public shared stack, the
exact exposure this change closes. Verify audit records on a deployment
you operate.
| chainID required | string <uuid> Decision chain id (the decision UUID) |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "chain_id": "01c3b16e-4d9b-4398-a3ec-0a1f8f0bb259",
- "org_id": "string",
- "total_records": 0,
- "valid": true,
- "authorship_proven": true,
- "linkage_valid": true,
- "signatures_valid": true,
- "signed_records": 0,
- "unsigned_records": 0,
- "first_broken_seq": 0,
- "first_broken_record_id": "string",
- "break_reason": "string",
- "signing_key_id": "string",
- "public_key": "string",
- "verified_at": "2019-08-24T14:15:22Z"
}Verifies ONE decision record standalone, proving its authorship from the record alone without walking the rest of the chain. Read-only.
The response republishes the recomputed verification material so the
result is independently checkable offline: digest_preimage_b64 is the
exact byte string that SHA-256-hashes to record_digest; an auditor can
base64-decode it, hash it, confirm it equals record_digest, rebuild it
from the raw record fields, and then run
ed25519.Verify(public_key, []byte(chain_hash), base64decode(record_signature)),
trusting neither this endpoint nor its digest.
The record is resolved by id only within the authenticated caller's
organization (RLS-scoped). recordID is the per-record UUID assigned at
signing time (distinct from the chain id).
Authority (#2914). Authentication alone is not enough: this route
requires COMPLIANCE READ AUTHORITY over the organization. Present a
validated per-user token (X-User-Token, or Authorization: Bearer)
whose role is admin, owner or policy_admin, or call from an
internal service asserting X-Axonflow-Admin-Authority: true. A
Community-mode deployment is exempt: it has no authentication and
resolves every caller to one local operator. Anything else is 403.
Community SaaS (try.getaxonflow.com) refuses these routes, by
design. The hosted evaluation stack runs with authentication enabled,
so the Community-mode exemption above does not apply to it, and its
evaluator accounts hold no entitled role. Verification there is a 403.
Granting evaluators access would widen, on a public shared stack, the
exact exposure this change closes. Verify audit records on a deployment
you operate.
| recordID required | string <uuid> Decision record id (the per-record UUID) |
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "record_id": "8bf519b6-a3e0-49d2-8e42-039542d9a489",
- "chain_id": "01c3b16e-4d9b-4398-a3ec-0a1f8f0bb259",
- "org_id": "string",
- "chain_seq": 0,
- "signed": true,
- "signature_valid": true,
- "valid": true,
- "reason": "string",
- "digest_preimage_b64": "string",
- "record_digest": "string",
- "prev_hash": "string",
- "chain_hash": "string",
- "record_signature": "string",
- "signing_key_id": "string",
- "public_key": "string",
- "verified_at": "2019-08-24T14:15:22Z"
}Returns the current public Ed25519 verification key so an external auditor can re-verify any record's signature offline. Read-only.
When no signing key is configured, configured is false and
public_key is empty: records are hash-chained but unsigned, and the
verify endpoints report that honestly.
Authority (#2914). Authentication alone is not enough: this route
requires COMPLIANCE READ AUTHORITY over the organization. Present a
validated per-user token (X-User-Token, or Authorization: Bearer)
whose role is admin, owner or policy_admin, or call from an
internal service asserting X-Axonflow-Admin-Authority: true. A
Community-mode deployment is exempt: it has no authentication and
resolves every caller to one local operator. Anything else is 403.
Community SaaS (try.getaxonflow.com) refuses these routes, by
design. The hosted evaluation stack runs with authentication enabled,
so the Community-mode exemption above does not apply to it, and its
evaluator accounts hold no entitled role. Verification there is a 403.
Granting evaluators access would widen, on a public shared stack, the
exact exposure this change closes. Verify audit records on a deployment
you operate.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "algorithm": "ed25519",
- "signing_key_id": "string",
- "public_key": "string",
- "configured": true,
- "verification_note": "string"
}Streamable-HTTP MCP server (spec 2025-06-18) exposing AxonFlow
governance as MCP tools (check_policy, check_output, audit_tool_call,
list_policies, get_policy_stats, explain_decision + Pro tools).
Consumed by the Claude Code / Cursor / Codex plugins and any MCP
client. Authentication is HTTP Basic (org:license-key) — NOT OAuth;
the /.well-known/oauth-* discovery paths deliberately return an
advisory 404 saying so.
Always answers 404 with an OAuth-shaped JSON error stating that this
server uses HTTP Basic authentication. No authorization server is
advertised. Any path under this prefix answers identically.
{- "error": "oauth_not_supported",
- "error_description": "AxonFlow's MCP server uses HTTP Basic authentication (base64(org_id:license_key)), not OAuth. Set AXONFLOW_ENDPOINT and AXONFLOW_AUTH in the environment that launches your MCP client."
}Always answers 404 with an OAuth-shaped JSON error stating that this
server uses HTTP Basic authentication. Any path under this prefix
answers identically.
{- "error": "oauth_not_supported",
- "error_description": "AxonFlow's MCP server uses HTTP Basic authentication (base64(org_id:license_key)), not OAuth. Set AXONFLOW_ENDPOINT and AXONFLOW_AUTH in the environment that launches your MCP client."
}Single Streamable-HTTP endpoint for the built-in MCP server. Accepts
MCP JSON-RPC 2.0 messages (initialize, tools/list, tools/call,
…) and returns JSON-RPC responses. Governance tools exposed:
check_policy, check_output, audit_tool_call, list_policies,
get_policy_stats, explain_decision, plus Pro-tier tools.
Authentication: HTTP Basic org:license-key on every request. An
Enterprise deployment MAY additionally send a per-user token in
X-User-Token (ADR-043/044). It is optional unless the organization's
require_user_token posture demands one (see BasicAuth).
Governance segments no longer gate check_policy or check_output
(they did from #3430, refusing with blocked_by: "segment_identity_unresolved" or "segment_resolution_failed").
Neither tool consults segments: each is decided by the anchored
engine, which reads none (PRD v11 §1.1, §1.2), so a segment
resolution that would fail changes nothing.
Two identity rules are narrower than an operator usually expects, and both are deliberate:
X-User-Email does not substitute for a validated per-user
token, even when AXONFLOW_TRUST_IDENTITY_HEADERS is on. Under
the organization's require_user_token posture, a caller without a
validated token is refused before the header can supply an
identity. The header
keeps its attribution role; the ADR-044 override writes answer
the freeze from v11.0.0 (#4252).create_override
refuses such a session for its identity first (every other session is
answered LEGACY_POLICY_WRITE_FROZEN from v11.0.0, #4252), and no
override is offered to it; its audit rows still carry the identity. The census is
IsSharedSyntheticIdentity in platform/shared/identity: the
reserved axonflow.local and axonflow.internal addresses, the MCP
client pseudo-identity prefix, and the Community-SaaS evaluator
[email protected]; in Community mode the local-dev
identity is exempt. Token minting validates only that
the subject contains @, so a token for such an identity
validates, and check_policy and check_output accept it like any
other validated token.The Community SaaS per-minute limit and daily quota are answered
with HTTP 429, a Retry-After header, and the RateLimitEnvelope as JSON text in
result.content[0].text, with result.isError: true (#4261;
before it, the same result came with a 200). The per-minute limit
answers limit_type: per_minute, window minute, resets_at one
minute out and Retry-After: 60; the daily quota answers
limit_type: daily_quota, window daily_utc, resetting at
midnight UTC. A credential refused by the pre-credential
per-minute limiter gets the same per-minute answer, with an empty
tier because the tier is not resolved yet, instead of a 401.
The tools/call tier gates (feature_pro_only, active_policies,
hitl_approvals_window) answer the same result shape with a 200.
JSON-RPC error codes: -32001 is the one application code, an
authentication failure, sent with HTTP 401 and a
WWW-Authenticate: Basic challenge. The rest are the JSON-RPC 2.0
standard codes: -32700 (parse error) with HTTP 400, -32600
(invalid request) with HTTP 400, or 415 when the Content-Type is
not application/json, and -32601 (method not found) and
-32602 (invalid params) with a 200.
The JSON-RPC check_output tool result DOES include
redacted_message — unlike the standalone REST
/api/v1/mcp/check-output endpoint (#2870).
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| jsonrpc | string Value: "2.0" |
| id | any Request ID (absent for notifications) |
| method | string |
object |
{- "jsonrpc": "2.0",
- "id": null,
- "method": "tools/call",
- "params": { }
}{ }Terminates the MCP session identified by the Mcp-Session-Id header.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
| Mcp-Session-Id | string |
{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}OpenTelemetry OTLP/HTTP ingest for Claude Code (cowork) telemetry (#2832). Enterprise only — community builds mount the routes but return 501. Org/tenant identity always comes from the authenticated license, never from OTLP resource attributes.
OTLP/HTTP ExportMetricsServiceRequest ingest for Claude Code
telemetry. Content types: application/x-protobuf (default when
the header is empty), application/protobuf, or application/json;
anything else returns 415. Success returns 200 with an OTLP
ExportMetricsServiceResponse in the request's content type.
Enterprise only — community builds return 501 with
{"error": {"code", "message"}}. Org/tenant comes from the
authenticated license (Basic auth), never from OTLP resource
attributes.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}OTLP/HTTP ExportLogsServiceRequest ingest for Claude Code
telemetry. Same content-type, auth, edition, and error semantics as
POST /v1/metrics.
| Authorization required | string Example: Basic bXktb3JnOkFYT04tVjIteHh4 OAuth2-style Basic authentication header.
Format:
Not required when |
{- "success": false,
- "error": "Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)"
}The AuthZEN-native authorization surface (ADR-065 compatibility plan, issue #3603). A Policy Enforcement Point sends a subject / action / resource / context evaluation and receives an AuthZEN decision.
This endpoint is an ADAPTER over the same evaluation that serves
POST /api/v1/decide. It is not a second policy engine. Through
v10.x the verdict is produced by today's evaluator; at v11 the engine
behind this route changes to the ADR-065 Policy Decision Point with
no wire change, so an integration written against this surface
migrates once rather than twice. POST /api/v1/decide is unchanged and
remains wire-stable through all of v11.
One route, both shapes. The body is the AuthZEN envelope, which
carries exactly one of evaluation (singular) or evaluations
(plural). Presence is decided on the key set, so
{"evaluation": {...}, "evaluations": null} carries both declared
members and is malformed.
A plural envelope returns one decision, not a list. Its entries are preconditions of a single operation, so they meet: one denied entry denies the operation. A plural entry inherits any member it omits from the envelope's shared base.
Every construct is mapped or refused; nothing is ignored. A request
carrying data this surface cannot evaluate -- caller-supplied
properties, an unrecognised context member, an argument beside the
query -- is REFUSED with a structured error naming the offending JSON
Pointer. It is never evaluated-and-ignored: reporting that a fact was
weighed when it was not is a fail-open, and every audit of such a
decision would inherit the claim.
A refusal is a different shape from a decision
(AuthZENError, no decision member), because a request that was never
evaluated must not be indistinguishable from one that was evaluated and
denied.
Profile negotiation. AuthZEN 1.0's response is a bare boolean.
What AxonFlow adds -- the four-valued state, obligations, the safe
reason code -- rides in context and is
returned ONLY to a caller that sent the
X-Axonflow-AuthZEN-Profile header naming a profile version this build
emits. A caller that did not negotiate receives the boolean alone; a
caller that named a version this build does not emit is refused with
406 rather than answered with the boolean, because a silent fallback
would report that the negotiation succeeded.
What the context does NOT carry, stated so you do not wait for it.
There is no approval challenge on this surface. The route is an
adapter over POST /api/v1/decide, and that response names no
eligible approver set, no quorum and no challenge expiry -- so there is
no requirement to render, and synthesising one would hand your
enforcement point a fabricated approval policy to enforce. A decision
awaiting approval arrives as state: CHALLENGE with decision: false;
act on it by holding, and obtain the challenge itself from the
human-approval surfaces. Earlier revisions of this document and of the
authzen_evaluation capability entry promised the challenge here, and
no response ever carried one.
The one case where not negotiating changes the ANSWER, not just the
shape. An evaluation that would otherwise be ALLOW but carries a
mandatory obligation is answered {"decision": false} to a caller
that did not negotiate the profile. The obligation is a precondition of
the permission, not a decoration on it, and it rides in the context
such a caller does not receive -- so returning true would hand out a
permission whose condition the enforcement point never sees and cannot
discharge. ADR-065 invariant 8 prescribes deny for a mandatory
obligation an enforcement point cannot enforce, and one that cannot
receive it is the limiting case. Everything else is unchanged for a
bare AuthZEN 1.0 caller: an allow carrying no mandatory obligation is
still true, and the denied, challenged and errored paths are
untouched. Negotiate the profile if you can discharge obligations --
a caller that sends the header gets the true and the obligation
together. Operationally the denial is counted under
outcome="obligation_withheld" on axonflow_authzen_requests_total,
logged, and audited as blocked with
policy_details.authzen_obligation_withheld, so an operator can find
the integrations that need the header.
Available at all tiers -- the evaluation behind it is the same one Decision Mode uses.
| X-Axonflow-Client | string Example: mcp-proxy/0.3.1 Optional client-version telemetry header ( |
| X-Axonflow-PEP-Handshake | string <= 4096 characters The ADR-065 PEP capability handshake: base64url of a compact JSON
document in which an external enforcement point declares what it is and
which obligations it can discharge. See Absent is the default and changes nothing. A caller that omits the header takes byte-for-byte the path it took before this header existed. A header that is PRESENT and cannot be read is refused, never
treated as absent: degrading a malformed declaration to "legacy caller"
would go on handing an enforcement point obligations it had just said it
cannot discharge. The refusal is Present more than once is refused: RFC 7230 permits an intermediary to join repeated field lines with a comma, and a comma is outside the base64 alphabet, so a joined pair can only decode to malformed. That is why the document is base64 rather than raw JSON, which would join into something a lenient parser might accept. When a decision carries a mandatory obligation the declared set does
not cover, the request is answered |
| X-Axonflow-AuthZEN-Profile | string Example: axonflow-authzen-profile-2026-08-29 The AxonFlow AuthZEN profile the caller can interpret. Send
|
| traceparent | string W3C trace-context header. When present and valid the trace-id is reused so multi-layer decisions correlate. |
required | object The singular member. It has no shared base to inherit from, so it must carry its own subject, action and resource. |
object (AuthZENBulk) The plural envelope. The decision count is fixed by the mapping, never
by argument data, so an empty |
{- "evaluation": {
- "subject": {
- "type": "gateway",
- "id": "llm-gateway-01"
}, - "action": {
- "name": "llm.completion"
}, - "resource": {
- "type": "llm",
- "id": "llm"
}, - "context": {
- "args": {
- "query": "summarise this ticket"
}
}
}
}{- "decision": true,
- "context": {
- "profile": "axonflow-authzen-profile-2026-08-29",
- "state": "ALLOW",
- "category": "allowed",
- "reason": "permitted",
- "decision_id": "6f1c2f2e-64f7-4f3e-93a1-2b5f0b0d1a77",
- "schema_version": "2026-08-29"
}
}