openapi: 3.2.0 info: title: Axonflow Bulk Operations API version: 11.1.0 contact: name: AxonFlow Support url: https://getaxonflow.com/support license: name: Business Source License 1.1 url: https://github.com/getaxonflow/axonflow/blob/main/LICENSE description: 'Operations tagged Bulk Operations across 2 of this provider''s published API definitions: axonflow-policy-api.yaml, axonflow-policy-openapi.yml. Each path carries the servers of the definition it was published in.' servers: - url: https://agent.getaxonflow.com description: Production (Agent single entry point, ADR-024) - url: http://localhost:8080 description: Local development (Agent single entry point) - url: http://localhost:8081 description: Orchestrator direct (internal only) tags: - name: Bulk Operations description: Import and export policies paths: /api/v1/policies/import: post: deprecated: true tags: - Bulk Operations summary: Import policies description: 'Bulk import policies from JSON. Supports up to 100 policies per request. Overwrite modes: - `skip`: Skip policies that already exist (by name) - `overwrite`: Update existing policies - `error`: Fail if any policy already exists On a deployment whose database connection cannot write the legacy policy tables (migrations/core/172), the request is refused `409 LEGACY_POLICY_WRITE_FROZEN` before its body is read, whatever it contains, and the refusal names the typed authoring route. An owner-role deployment still imports.' operationId: importPolicies parameters: - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/UserID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImportPoliciesRequest' responses: '200': description: Import results content: application/json: schema: $ref: '#/components/schemas/ImportPoliciesResponse' '400': $ref: '#/components/responses/ValidationError' '401': $ref: '#/components/responses/Unauthorized' '409': $ref: '#/components/responses/LegacyPolicyWriteFrozen' '500': $ref: '#/components/responses/InternalError' servers: - url: https://agent.getaxonflow.com description: Production (Agent single entry point, ADR-024) - url: http://localhost:8080 description: Local development (Agent single entry point) - url: http://localhost:8081 description: Orchestrator direct (internal only) /api/v1/policies/export: get: deprecated: true tags: - Bulk Operations summary: Export policies description: Export all policies for the tenant as JSON operationId: exportPolicies parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Exported policies headers: Content-Disposition: schema: type: string description: Attachment filename example: attachment; filename=policies-export.json content: application/json: schema: $ref: '#/components/schemas/ExportPoliciesResponse' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalError' servers: - url: https://agent.getaxonflow.com description: Production (Agent single entry point, ADR-024) - url: http://localhost:8080 description: Local development (Agent single entry point) - url: http://localhost:8081 description: Orchestrator direct (internal only) components: responses: Unauthorized: description: Missing or invalid tenant ID content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: code: UNAUTHORIZED message: Missing tenant ID ValidationError: description: Request validation failed content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: code: VALIDATION_ERROR message: Request validation failed details: - field: name message: Name must be between 3 and 100 characters - field: conditions[0].operator message: 'Invalid operator: like. Must be one of: equals, contains, regex' InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: code: INTERNAL_ERROR message: An unexpected error occurred LegacyPolicyWriteFrozen: description: 'The legacy policy tables are read-only. `migrations/core/172` revoked INSERT, UPDATE, DELETE and TRUNCATE on `static_policies` and `dynamic_policies` from the application roles, and this endpoint writes them, so the write is refused permanently rather than transiently. It is not an entitlement fact: no licence, edition or upgrade changes it. Author policies through the typed authoring route (`/api/v1/typed-policies`, in `orchestrator-api.yaml`) instead. Reads on this endpoint are unaffected. A deployment whose connection may still write the tables (the database owner; a property of the connection, not of `AXONFLOW_DB_USE_APP_ROLE` alone) is not bound by the revoke and does not receive this response. Where the connection cannot write them, the create, update and bulk-import routes answer this before reading the request body, whatever it contains. ' content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: code: LEGACY_POLICY_WRITE_FROZEN message: 'The legacy policy tables are read-only in v11: migrations/core/172 revoked write access from the application role, and this endpoint writes them. Author policies through the typed authoring route at /api/v1/typed-policies instead. Reads on this endpoint are unaffected.' parameters: UserID: name: X-User-ID in: header required: false description: User identifier for audit logging schema: type: string example: user@company.com TenantID: name: X-Tenant-ID in: header required: true description: 'Tenant identifier for multi-tenancy isolation. When calling through the Agent (recommended), this header is stamped automatically from the authenticated client — you do not set it yourself. Required only on direct Orchestrator calls (internal deployments). ' schema: type: string example: tenant_abc123 schemas: PolicyResource: type: object properties: id: type: string description: Unique policy identifier example: pol_abc123def456 name: type: string description: Human-readable policy name minLength: 3 maxLength: 100 example: Block PII Access description: type: string description: Detailed policy description maxLength: 500 example: Prevent unauthorized access to personally identifiable information type: $ref: '#/components/schemas/PolicyType' category: type: string description: Policy category (dynamic-risk, dynamic-compliance, media-safety, etc.) example: dynamic-risk tier: type: string enum: - system - organization - tenant description: Policy tier in the hierarchy (system policies are immutable) conditions: type: array items: $ref: '#/components/schemas/PolicyCondition' minItems: 1 description: Conditions that must all match (AND logic) actions: type: array items: $ref: '#/components/schemas/PolicyAction' minItems: 1 description: Actions to execute when policy matches priority: type: integer minimum: 0 maximum: 1000 default: 0 description: Higher priority policies are evaluated first enabled: type: boolean default: true description: Whether the policy is active version: type: integer minimum: 1 description: Policy version number, incremented on each update tenant_id: type: string description: Owning tenant ID organization_id: type: string description: 'The organisation that owns this policy. Since #3490 the organisation is what SELECTS a policy row, at every tier -- this is not an organization-tier-only field, and the previous wording described the retired organization_id COLUMN rather than this resource field. ' tags: type: array items: type: string description: Tags for categorization created_at: type: string format: date-time description: Creation timestamp updated_at: type: string format: date-time description: Last update timestamp created_by: type: string description: User who created the policy updated_by: type: string description: User who last updated the policy deleted_at: type: string format: date-time description: Soft-delete timestamp (present only on deleted policies) ImportPoliciesRequest: type: object required: - policies properties: policies: type: array items: $ref: '#/components/schemas/CreatePolicyRequest' minItems: 1 maxItems: 100 overwrite_mode: type: string enum: - skip - overwrite - error default: skip description: How to handle existing policies PolicyType: type: string enum: - content - user - risk - cost - context_aware - media - rate-limit - budget - time-access - role-access - mcp - connector description: 'Policy type determines evaluation context: - `content`: Evaluates request/response content - `user`: Evaluates user attributes - `risk`: Evaluates risk scores - `cost`: Evaluates cost estimates - `context_aware`: Context-aware controls (tenant isolation, debug restriction, sensitive-data control) - `media`: Media governance policies (multimodal image governance) - `rate-limit`, `budget`, `time-access`: MCP rate/budget controls - `role-access`, `mcp`, `connector`: MCP access controls ' APIError: type: object properties: error: type: object properties: code: type: string description: Error code message: type: string description: Human-readable error message details: type: array items: type: object properties: field: type: string message: type: string description: Field-level validation errors ExportPoliciesResponse: type: object properties: policies: type: array items: $ref: '#/components/schemas/PolicyResource' exported_at: type: string format: date-time tenant_id: type: string CreatePolicyRequest: type: object required: - name - type - conditions - actions properties: name: type: string minLength: 3 maxLength: 100 description: type: string maxLength: 500 type: $ref: '#/components/schemas/PolicyType' category: type: string description: 'Policy category (dynamic-risk, dynamic-compliance, media-safety, etc.). Required on the /api/v1/dynamic-policies surface, where it must start with `dynamic-` or `media-`. ' tier: type: string enum: - organization - tenant description: Policy tier. Only organization or tenant is allowed via the API. conditions: type: array items: $ref: '#/components/schemas/PolicyCondition' minItems: 1 actions: type: array items: $ref: '#/components/schemas/PolicyAction' minItems: 1 priority: type: integer minimum: 0 maximum: 1000 default: 0 enabled: type: boolean default: true tags: type: array items: type: string description: Tags for categorization ActionType: type: string enum: - block - require_approval - redact - warn - alert - log - route - modify_risk description: 'Action to take when policy matches: - `block`: Block the request with message - `require_approval`: Hold the request for human approval (HITL) - `redact`: Redact sensitive content from response - `warn`: Allow the request but attach a warning - `alert`: Send alert to configured channel - `log`: Log to audit trail - `route`: Route to specific provider - `modify_risk`: Adjust risk score ' ConditionOperator: type: string enum: - equals - not_equals - contains - not_contains - contains_any - regex - greater_than - less_than - in - not_in description: Comparison operator for conditions ImportPoliciesResponse: type: object properties: created: type: integer description: Number of policies created updated: type: integer description: Number of policies updated skipped: type: integer description: Number of policies skipped errors: type: array items: type: string description: Error messages for failed imports PolicyCondition: type: object required: - field - operator - value properties: field: type: string description: 'Field to evaluate. `media.*` fields apply to media governance policies (multimodal image governance); `step.*` fields are retry-aware workflow step fields for WCP policies. ' enum: - query - response - user.email - user.role - user.department - user.tenant_id - risk_score - request_type - connector - cost_estimate - media.has_faces - media.face_count - media.has_biometric_data - media.nsfw_score - media.violence_score - media.content_safe - media.document_type - media.is_sensitive_document - media.has_pii - media.pii_types - media.has_extracted_text - media.extracted_text_length - step.gate_count - step.completion_count - step.prior_completion_status - step.prior_output_available - step.last_decision - step.first_attempt_age_seconds - step.idempotency_key operator: $ref: '#/components/schemas/ConditionOperator' value: oneOf: - type: string - type: number - type: boolean - type: array items: type: string description: Value to compare against PolicyAction: type: object required: - type properties: type: $ref: '#/components/schemas/ActionType' config: type: object additionalProperties: true description: Action-specific configuration example: message: Request blocked by policy channel: security-alerts x-refined-from: - axonflow-policy-api.yaml - axonflow-policy-openapi.yml