openapi: 3.0.3 info: title: Cordum HTTP API version: '2026-04-21.2' description: Canonical OpenAPI 3.0.3 spec for the Cordum gateway HTTP surface. contact: name: Cordum Team license: name: Proprietary servers: - url: http://localhost:8081 description: Gateway HTTP (local default) - url: / description: Relative gateway base tags: - name: Auth description: Authentication and session management - name: Users description: User CRUD (admin only) - name: APIKeys description: API key lifecycle (admin only) - name: Jobs description: Job submission, listing, and lifecycle - name: DLQ description: Dead-letter queue management (admin only) - name: Workflows description: Workflow definition CRUD and execution - name: WorkflowRuns description: Workflow run inspection and control - name: Approvals description: Human-in-the-loop approval queue (admin only) - name: Policy description: Safety policy evaluation, bundles, and snapshots - name: Config description: Hierarchical configuration management - name: Schemas description: JSON schema registry - name: Locks description: Distributed lock management - name: Packs description: Pack installation and management (admin only) - name: Marketplace description: Pack marketplace catalog (admin only) - name: Health description: Health checks and system status - name: Memory description: Memory pointers and artifact storage - name: MCP description: Model Context Protocol (SSE + JSON-RPC) - name: Streaming description: WebSocket event streaming - name: Workers description: Worker agent registration, status, and active job queries - name: WorkerCredentials description: Manage per-worker authentication credentials (admin only) - name: Pools description: Worker pool configuration, topic assignments, and drain operations - name: Topics description: Topic registry for job routing and worker discovery - name: License description: Platform license status, usage metrics, and reload (admin only) - name: Telemetry description: Observability export configuration, consent, and usage stats - name: AuditExport description: Cryptographic audit log export configuration and health checks (admin only) - name: LegalHold description: Legal hold management for audit retention compliance (admin only) - name: Governance description: Governance-native observability — policy decisions, approval analytics - name: Agents description: Agent identities, delegation, and runtime statistics - name: Delegation description: Delegation token issuance, verification, listing, and revocation - name: Evals description: Evaluation dataset curation, incident extraction, and policy replay runs security: - apiKey: [] - bearerAuth: [] paths: /api/v1/auth/config: get: operationId: getAuthConfig summary: Get authentication configuration description: Returns the authentication methods enabled for the deployment. tags: - Auth security: [] responses: '200': description: Auth configuration content: application/json: schema: $ref: '#/components/schemas/AuthConfig' '500': $ref: '#/components/responses/InternalServerError' /api/v1/auth/login: post: operationId: login summary: Authenticate with username and password tags: - Auth security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': description: Authentication successful content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalServerError' /api/v1/auth/session: get: operationId: getSession summary: Get current session description: Returns the authenticated user and token metadata. tags: - Auth parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Current session content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/auth/logout: post: operationId: logout summary: Logout and invalidate session tags: - Auth parameters: - $ref: '#/components/parameters/TenantID' responses: '204': description: Logged out '401': $ref: '#/components/responses/Unauthorized' /api/v1/auth/password: post: operationId: changeOwnPassword summary: Change own password tags: - Auth parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChangePasswordRequest' responses: '204': description: Password changed '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/users: post: operationId: createUser summary: Create a user tags: - Users parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: User created content: application/json: schema: $ref: '#/components/schemas/AuthUser' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' get: operationId: listUsers summary: List all users tags: - Users parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: User list content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/AuthUser' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/users/{id}: parameters: - $ref: '#/components/parameters/UserID' - $ref: '#/components/parameters/TenantID' put: operationId: updateUser summary: Update a user tags: - Users requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateUserRequest' responses: '200': description: User updated content: application/json: schema: $ref: '#/components/schemas/AuthUser' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteUser summary: Delete a user tags: - Users responses: '204': description: User deleted '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/users/{id}/password: post: operationId: resetUserPassword summary: Reset a user's password (admin) tags: - Users parameters: - $ref: '#/components/parameters/UserID' - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: password: type: string format: password required: - password responses: '204': description: Password reset '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/auth/keys: get: operationId: listAPIKeys summary: List API keys tags: - APIKeys parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: API key list content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/APIKeyInfo' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' post: operationId: createAPIKey summary: Create an API key description: 'The raw secret is returned **only once** in the response. Store it securely; it cannot be retrieved again. ' tags: - APIKeys parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAPIKeyRequest' responses: '201': description: Key created content: application/json: schema: $ref: '#/components/schemas/CreateAPIKeyResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/auth/keys/{id}: delete: operationId: deleteAPIKey summary: Revoke an API key tags: - APIKeys parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '204': description: Key revoked '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs: post: operationId: submitJob summary: Submit a new job tags: - Jobs parameters: - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubmitJobRequest' responses: '200': description: Job accepted content: application/json: schema: $ref: '#/components/schemas/SubmitJobResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '413': $ref: '#/components/responses/PayloadTooLarge' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalServerError' get: operationId: listJobs summary: List jobs tags: - Jobs parameters: - $ref: '#/components/parameters/TenantID' - name: limit in: query schema: type: integer default: 50 - name: cursor in: query schema: type: string - name: state in: query schema: type: string - name: topic in: query schema: type: string - name: tenant in: query schema: type: string responses: '200': description: Job list content: application/json: schema: allOf: - $ref: '#/components/schemas/PaginatedResponse' - type: object properties: items: type: array items: $ref: '#/components/schemas/JobSummary' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs/{id}: get: operationId: getJob summary: Get job details tags: - Jobs parameters: - $ref: '#/components/parameters/JobID' - $ref: '#/components/parameters/TenantID' responses: '200': description: Job detail content: application/json: schema: $ref: '#/components/schemas/JobDetail' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs/{id}/decisions: get: operationId: getJobDecisions summary: Get safety decisions for a job tags: - Jobs parameters: - $ref: '#/components/parameters/JobID' - $ref: '#/components/parameters/TenantID' responses: '200': description: Safety decisions content: application/json: schema: type: array items: $ref: '#/components/schemas/SafetyDecision' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs/{id}/cancel: post: operationId: cancelJob summary: Cancel a running job tags: - Jobs parameters: - $ref: '#/components/parameters/JobID' - $ref: '#/components/parameters/TenantID' responses: '200': description: Job cancelled content: application/json: schema: type: object properties: id: type: string state: type: string example: CANCELLED '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs/{id}/remediate: post: operationId: remediateJob summary: Remediate a quarantined job tags: - Jobs parameters: - $ref: '#/components/parameters/JobID' - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: remediation_id: type: string required: - remediation_id responses: '200': description: Remediation submitted content: application/json: schema: $ref: '#/components/schemas/SubmitJobResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/jobs/{id}/stream: get: operationId: streamJob summary: Stream job events (WebSocket) description: 'Upgrades to a WebSocket connection and streams real-time events for the specified job until it reaches a terminal state. ' tags: - Jobs - Streaming parameters: - $ref: '#/components/parameters/JobID' - $ref: '#/components/parameters/TenantID' responses: '101': description: WebSocket upgrade '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/v1/traces/{id}: get: operationId: getTrace summary: Get trace records for a job tags: - Jobs parameters: - name: id in: path required: true description: Trace ID schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Trace records content: application/json: schema: type: array items: $ref: '#/components/schemas/JobDetail' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/dlq: get: operationId: listDLQ summary: List dead-letter queue entries tags: - DLQ parameters: - $ref: '#/components/parameters/TenantID' - name: limit in: query schema: type: integer default: 50 responses: '200': description: DLQ entries content: application/json: schema: type: array items: $ref: '#/components/schemas/DLQEntry' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/dlq/page: get: operationId: listDLQPaginated summary: List DLQ entries (paginated) tags: - DLQ parameters: - $ref: '#/components/parameters/TenantID' - name: limit in: query schema: type: integer default: 50 - name: cursor in: query schema: type: string responses: '200': description: Paginated DLQ entries content: application/json: schema: allOf: - $ref: '#/components/schemas/PaginatedResponse' - type: object properties: items: type: array items: $ref: '#/components/schemas/DLQEntry' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/dlq/{job_id}: delete: operationId: deleteDLQEntry summary: Remove a DLQ entry tags: - DLQ parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '204': description: Entry removed '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/dlq/{job_id}/retry: post: operationId: retryDLQEntry summary: Retry a dead-lettered job tags: - DLQ parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Retry enqueued content: application/json: schema: type: object properties: job_id: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflows: get: operationId: listWorkflows summary: List workflow definitions tags: - Workflows parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Workflow list content: application/json: schema: type: array items: $ref: '#/components/schemas/WorkflowDefinition' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' post: operationId: createWorkflow summary: Create or update a workflow definition tags: - Workflows parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WorkflowDefinition' responses: '201': description: Workflow created content: application/json: schema: type: object properties: id: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflows/{id}: parameters: - $ref: '#/components/parameters/WorkflowID' - $ref: '#/components/parameters/TenantID' get: operationId: getWorkflow summary: Get a workflow definition tags: - Workflows responses: '200': description: Workflow definition content: application/json: schema: $ref: '#/components/schemas/WorkflowDefinition' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteWorkflow summary: Delete a workflow definition tags: - Workflows responses: '204': description: Workflow deleted '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflows/{id}/runs: parameters: - $ref: '#/components/parameters/WorkflowID' - $ref: '#/components/parameters/TenantID' post: operationId: startWorkflowRun summary: Start a new workflow run tags: - Workflows requestBody: required: true content: application/json: schema: type: object description: Workflow input payload (schema depends on the workflow) additionalProperties: true responses: '200': description: Run started content: application/json: schema: type: object properties: run_id: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' get: operationId: listWorkflowRuns summary: List runs for a workflow tags: - Workflows responses: '200': description: Run list content: application/json: schema: type: array items: $ref: '#/components/schemas/RunSummary' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflows/{id}/dry-run: post: operationId: dryRunWorkflow summary: Dry-run a workflow without side effects tags: - Workflows parameters: - $ref: '#/components/parameters/WorkflowID' - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: input: type: object additionalProperties: true environment: type: object additionalProperties: true responses: '200': description: Dry-run result content: application/json: schema: $ref: '#/components/schemas/DryRunResult' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflow-runs: get: operationId: listAllWorkflowRuns summary: List workflow runs across all workflows tags: - WorkflowRuns parameters: - $ref: '#/components/parameters/TenantID' - name: limit in: query schema: type: integer default: 50 - name: cursor in: query schema: type: string - name: status in: query schema: type: string - name: workflow_id in: query schema: type: string responses: '200': description: Run list content: application/json: schema: allOf: - $ref: '#/components/schemas/PaginatedResponse' - type: object properties: items: type: array items: $ref: '#/components/schemas/RunSummary' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflow-runs/{id}: parameters: - $ref: '#/components/parameters/RunID' - $ref: '#/components/parameters/TenantID' get: operationId: getWorkflowRun summary: Get workflow run detail tags: - WorkflowRuns responses: '200': description: Run detail content: application/json: schema: $ref: '#/components/schemas/RunDetail' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteWorkflowRun summary: Delete a workflow run tags: - WorkflowRuns responses: '204': description: Run deleted '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflow-runs/{id}/timeline: get: operationId: getRunTimeline summary: Get timeline events for a run tags: - WorkflowRuns parameters: - $ref: '#/components/parameters/RunID' - $ref: '#/components/parameters/TenantID' responses: '200': description: Timeline events content: application/json: schema: type: array items: $ref: '#/components/schemas/TimelineEvent' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflow-runs/{id}/chat: parameters: - $ref: '#/components/parameters/RunID' - $ref: '#/components/parameters/TenantID' get: operationId: getRunChat summary: Get chat messages for a run tags: - WorkflowRuns responses: '200': description: Chat messages content: application/json: schema: allOf: - $ref: '#/components/schemas/PaginatedResponse' - type: object properties: items: type: array items: $ref: '#/components/schemas/ChatMessage' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' post: operationId: postRunChat summary: Post a chat message to a run tags: - WorkflowRuns requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PostChatRequest' responses: '200': description: Message posted content: application/json: schema: $ref: '#/components/schemas/ChatMessage' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflow-runs/{id}/rerun: post: operationId: rerunWorkflow summary: Re-run a workflow (optionally from a specific step) tags: - WorkflowRuns parameters: - $ref: '#/components/parameters/RunID' - $ref: '#/components/parameters/TenantID' requestBody: content: application/json: schema: type: object properties: from_step: type: string description: Step ID to resume from dry_run: type: boolean default: false responses: '200': description: Re-run started content: application/json: schema: type: object properties: run_id: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workflows/{id}/runs/{run_id}/cancel: post: operationId: cancelWorkflowRun summary: Cancel a running workflow run tags: - WorkflowRuns parameters: - $ref: '#/components/parameters/WorkflowID' - name: run_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '204': description: Run cancelled '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' /api/v1/approvals: get: operationId: listApprovals summary: List pending approvals tags: - Approvals parameters: - $ref: '#/components/parameters/TenantID' - name: limit in: query schema: type: integer default: 50 - name: cursor in: query schema: type: string responses: '200': description: Approval list content: application/json: schema: allOf: - $ref: '#/components/schemas/PaginatedResponse' - type: object properties: items: type: array items: $ref: '#/components/schemas/ApprovalItem' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/approvals/{job_id}/approve: post: operationId: approveJob summary: Approve a job tags: - Approvals parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' responses: '200': description: Job approved content: application/json: schema: $ref: '#/components/schemas/SubmitJobResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/approvals/{job_id}/reject: post: operationId: rejectJob summary: Reject a job tags: - Approvals parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' responses: '200': description: Job rejected content: application/json: schema: type: object properties: job_id: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/approvals/{job_id}/context: get: operationId: getApprovalContext summary: Get enriched approval context for a single job description: > Returns all enriched context for a single approval in one call: blast radius, prior approvals, rollback hints, policy snapshot summary, time remaining, and parsed constraints. Used by the approval detail page. tags: - Approvals parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Enriched approval context content: application/json: schema: type: object properties: approval: type: object description: Full approval record with decision summary and workflow metadata blast_radius: type: object properties: systems: type: array items: type: string namespaces: type: array items: type: string resources: type: array items: type: string scope_description: type: string prior_approvals: type: array items: type: object properties: job_id: type: string topic: type: string tenant: type: string decision: type: string resolved_by: type: string resolved_at: type: integer format: int64 was_approved: type: boolean rollback_hint: type: string description: Pack-provided rollback instructions policy_snapshot_summary: type: object properties: rule_count: type: integer matched_rule: type: object properties: id: type: string description: type: string decision: type: string constraints_summary: type: string policy_version: type: string time_remaining_ms: type: integer format: int64 nullable: true description: Milliseconds until approval deadline, null if no deadline constraints: type: object nullable: true description: Parsed policy constraints from safety decision '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/evaluate: post: operationId: evaluatePolicy summary: Evaluate safety policy against a request tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyCheckRequest' responses: '200': description: Policy evaluation result content: application/json: schema: $ref: '#/components/schemas/PolicyCheckResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/simulate: post: operationId: simulatePolicy summary: Simulate policy evaluation (no side effects) tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyCheckRequest' responses: '200': description: Simulation result content: application/json: schema: $ref: '#/components/schemas/PolicyCheckResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/explain: post: operationId: explainPolicy summary: Explain policy evaluation reasoning tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyCheckRequest' responses: '200': description: Explanation result content: application/json: schema: $ref: '#/components/schemas/PolicyCheckResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/snapshots: get: operationId: listPolicySnapshots summary: List policy snapshots tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Snapshot list content: application/json: schema: type: array items: $ref: '#/components/schemas/PolicySnapshot' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/rules: get: operationId: listPolicyRules summary: List policy rules tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' - name: include_disabled in: query schema: type: boolean default: false responses: '200': description: Policy rules content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/PolicyRule' errors: type: array items: type: string '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/output/rules: get: operationId: listOutputRules summary: List output policy rules tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Output rules content: application/json: schema: type: array items: $ref: '#/components/schemas/OutputRule' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/output/rules/{id}: put: operationId: upsertOutputRule summary: Create or update an output policy rule tags: - Policy parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OutputRule' responses: '200': description: Rule upserted content: application/json: schema: $ref: '#/components/schemas/OutputRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/bundles: get: operationId: listPolicyBundles summary: List policy bundles tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Bundle list content: application/json: schema: type: object properties: bundles: type: array items: $ref: '#/components/schemas/PolicyBundleSummary' items: type: array items: $ref: '#/components/schemas/PolicyBundleSummary' updated_at: type: string format: date-time '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/bundles/{id}: parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' get: operationId: getPolicyBundle summary: Get a policy bundle with content tags: - Policy responses: '200': description: Bundle detail content: application/json: schema: $ref: '#/components/schemas/PolicyBundleDetail' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' put: operationId: updatePolicyBundle summary: Update a policy bundle tags: - Policy requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdatePolicyBundleRequest' responses: '200': description: Bundle updated content: application/json: schema: $ref: '#/components/schemas/PolicyBundleDetail' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deletePolicyBundle summary: Delete a policy bundle tags: - Policy responses: '204': description: Policy bundle deleted '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/bundles/{id}/simulate: post: operationId: simulatePolicyBundle summary: Simulate a policy bundle against a request tags: - Policy parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: request: $ref: '#/components/schemas/PolicyCheckRequest' content: type: string description: Optional override bundle YAML content responses: '200': description: Simulation result content: application/json: schema: $ref: '#/components/schemas/PolicyCheckResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/bundles/snapshots: get: operationId: listBundleSnapshots summary: List policy bundle snapshots tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Snapshot list content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/PolicySnapshot' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' post: operationId: createBundleSnapshot summary: Create a policy bundle snapshot tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: note: type: string responses: '200': description: Snapshot created content: application/json: schema: $ref: '#/components/schemas/PolicySnapshot' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/bundles/snapshots/{id}: get: operationId: getBundleSnapshot summary: Get a policy bundle snapshot tags: - Policy parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Snapshot detail content: application/json: schema: $ref: '#/components/schemas/PolicySnapshot' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/publish: post: operationId: publishPolicy summary: Publish policy bundles to production tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PublishPolicyRequest' responses: '200': description: Policy published content: application/json: schema: $ref: '#/components/schemas/PolicySnapshot' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/rollback: post: operationId: rollbackPolicy summary: Rollback policy to a previous snapshot tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RollbackPolicyRequest' responses: '200': description: Policy rolled back content: application/json: schema: $ref: '#/components/schemas/PolicySnapshot' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/audit: get: operationId: getPolicyAudit summary: Get policy audit log tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Audit items content: application/json: schema: type: array items: $ref: '#/components/schemas/PolicyAuditEntry' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/config: get: operationId: getConfig summary: Get configuration tags: - Config parameters: - $ref: '#/components/parameters/TenantID' - name: scope in: query schema: type: string enum: - system - org - team - workflow - step - name: scope_id in: query schema: type: string - name: envelope in: query description: Whether to wrap in {scope, data} envelope schema: type: boolean default: true responses: '200': description: Configuration data content: application/json: schema: $ref: '#/components/schemas/ConfigDocument' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' post: operationId: setConfig summary: Set configuration tags: - Config parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfigDocument' responses: '204': description: Configuration saved '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' put: operationId: replaceConfig summary: Replace configuration tags: - Config parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfigDocument' responses: '204': description: Configuration saved '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/config/effective: get: operationId: getEffectiveConfig summary: Get merged effective configuration description: 'Returns the effective configuration after merging all scopes (global -> org -> team -> workflow -> step). ' tags: - Config parameters: - $ref: '#/components/parameters/TenantID' - name: org_id in: query schema: type: string - name: team_id in: query schema: type: string - name: workflow_id in: query schema: type: string - name: step_id in: query schema: type: string responses: '200': description: Effective configuration content: application/json: schema: $ref: '#/components/schemas/ConfigDocument' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/schemas: post: operationId: createSchema summary: Register a JSON schema tags: - Schemas parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SchemaRecord' responses: '204': description: Schema registered '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' get: operationId: listSchemas summary: List registered schemas tags: - Schemas parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Schema list content: application/json: schema: type: object properties: schemas: type: array items: $ref: '#/components/schemas/SchemaRecord' required: - schemas '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/schemas/{id}: parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' get: operationId: getSchema summary: Get a schema by ID tags: - Schemas responses: '200': description: Schema detail content: application/json: schema: $ref: '#/components/schemas/SchemaRecord' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteSchema summary: Delete a schema tags: - Schemas responses: '204': description: Schema deleted '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/locks: get: operationId: getLock summary: Get lock status for a resource tags: - Locks parameters: - $ref: '#/components/parameters/TenantID' - name: resource in: query required: true schema: type: string responses: '200': description: Lock information content: application/json: schema: $ref: '#/components/schemas/Lock' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/locks/acquire: post: operationId: acquireLock summary: Acquire a distributed lock tags: - Locks parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LockRequest' responses: '200': description: Lock acquired content: application/json: schema: $ref: '#/components/schemas/Lock' '409': $ref: '#/components/responses/Conflict' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/locks/release: post: operationId: releaseLock summary: Release a distributed lock tags: - Locks parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LockRequest' responses: '200': description: Lock released content: application/json: schema: type: object properties: lock: $ref: '#/components/schemas/Lock' released: type: boolean '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/locks/renew: post: operationId: renewLock summary: Renew a distributed lock TTL tags: - Locks parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LockRequest' responses: '200': description: Lock renewed content: application/json: schema: $ref: '#/components/schemas/Lock' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/packs: get: operationId: listPacks summary: List installed packs tags: - Packs parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Pack list content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/PackRecord' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/packs/{id}: get: operationId: getPack summary: Get a pack by ID tags: - Packs parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Pack detail content: application/json: schema: $ref: '#/components/schemas/PackRecord' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/packs/install: post: operationId: installPack summary: Install a pack from a bundle file tags: - Packs parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: multipart/form-data: schema: type: object properties: bundle: type: string format: binary description: Pack bundle archive required: - bundle responses: '200': description: Pack installed content: application/json: schema: $ref: '#/components/schemas/PackRecord' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/PayloadTooLarge' '500': $ref: '#/components/responses/InternalServerError' /api/v1/packs/{id}/uninstall: post: operationId: uninstallPack summary: Uninstall a pack tags: - Packs parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: content: application/json: schema: type: object properties: purge: type: boolean description: Whether to purge all pack data default: false responses: '200': description: Pack uninstalled content: application/json: schema: $ref: '#/components/schemas/PackRecord' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/packs/{id}/verify: post: operationId: verifyPack summary: Verify a pack's integrity tags: - Packs parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Verification results content: application/json: schema: $ref: '#/components/schemas/PackVerification' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/marketplace/packs: get: operationId: listMarketplacePacks summary: List packs available in the marketplace tags: - Marketplace parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Marketplace catalog content: application/json: schema: $ref: '#/components/schemas/MarketplaceCatalog' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '502': $ref: '#/components/responses/BadGateway' '500': $ref: '#/components/responses/InternalServerError' /api/v1/marketplace/install: post: operationId: installMarketplacePack summary: Install a pack from the marketplace tags: - Marketplace parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MarketplaceInstallRequest' responses: '200': description: Pack installed content: application/json: schema: $ref: '#/components/schemas/PackRecord' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '502': $ref: '#/components/responses/BadGateway' '500': $ref: '#/components/responses/InternalServerError' /health: get: operationId: healthCheck summary: Basic health check tags: - Health security: [] responses: '200': description: Service is healthy content: text/plain: schema: type: string example: ok /api/v1/status: get: operationId: getStatus summary: Get system status tags: - Health parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: System status content: application/json: schema: $ref: '#/components/schemas/StatusResponse' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workers: get: operationId: listWorkers summary: List registered workers and heartbeats tags: - Workers parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Worker heartbeats content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/WorkerRuntime' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/memory: get: operationId: getMemory summary: Get memory data by pointer or key tags: - Memory parameters: - $ref: '#/components/parameters/TenantID' - name: ptr in: query description: Memory pointer schema: type: string - name: key in: query description: Memory key schema: type: string responses: '200': description: Memory data content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/artifacts: post: operationId: createArtifact summary: Upload an artifact tags: - Memory parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateArtifactRequest' responses: '200': description: Artifact stored content: application/json: schema: $ref: '#/components/schemas/CreateArtifactResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '413': $ref: '#/components/responses/PayloadTooLarge' '500': $ref: '#/components/responses/InternalServerError' /api/v1/artifacts/{ptr}: get: operationId: getArtifact summary: Get an artifact by pointer tags: - Memory parameters: - name: ptr in: path required: true description: Artifact pointer schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Artifact data content: application/json: schema: $ref: '#/components/schemas/ArtifactDetail' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /mcp/sse: get: operationId: mcpSSE summary: MCP Server-Sent Events stream description: 'Opens an SSE connection to the MCP server. The server sends JSON-RPC notifications and responses over this channel. ' tags: - MCP security: - apiKey: [] - bearerAuth: [] responses: '200': description: SSE stream opened content: text/event-stream: schema: type: string '503': $ref: '#/components/responses/ServiceUnavailable' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /mcp/message: post: operationId: mcpMessage summary: Send a JSON-RPC 2.0 message to MCP server tags: - MCP security: - apiKey: [] - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/JsonRpcRequest' responses: '200': description: JSON-RPC response content: application/json: schema: $ref: '#/components/schemas/JsonRpcResponse' '400': $ref: '#/components/responses/BadRequest' '503': $ref: '#/components/responses/ServiceUnavailable' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /mcp/status: get: operationId: mcpStatus summary: Get MCP server status tags: - MCP security: - apiKey: [] - bearerAuth: [] responses: '200': description: MCP status content: application/json: schema: $ref: '#/components/schemas/McpStatus' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /api/v1/stream: # The handler is registered with no HTTP method prefix (net/http # mux treats the pattern as any-method). The GET op below documents # the WebSocket upgrade path; x-any-method declares the handler-side # semantics so the openapi-audit tool doesn't flag the path as # under-specified. x-any-method: true get: operationId: streamEvents summary: WebSocket event bus stream description: 'Upgrades to a WebSocket connection. Authenticates via the `cordum-api-key.` subprotocol. Streams real-time bus events (job state changes, heartbeats, policy updates, etc.). Authentication: provide `X-Tenant-ID` and authenticate via `Sec-WebSocket-Protocol: cordum-api-key, `.' tags: - Streaming responses: '101': description: WebSocket upgrade '401': $ref: '#/components/responses/Unauthorized' /api/v1/health: get: operationId: healthCheckV1 summary: Gateway health check through the API namespace tags: - Health parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Service is healthy content: text/plain: schema: type: string example: ok '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/auth/roles: get: operationId: listRoles summary: List RBAC roles tags: - Auth parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Role inventory content: application/json: schema: $ref: '#/components/schemas/RoleListResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/auth/roles/{name}: parameters: - $ref: '#/components/parameters/RoleName' - $ref: '#/components/parameters/TenantID' get: operationId: getRole summary: Get a single RBAC role tags: - Auth responses: '200': description: Role detail content: application/json: schema: $ref: '#/components/schemas/RoleDetailResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' put: operationId: putRole summary: Create or update an RBAC role tags: - Auth requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleRequest' responses: '200': description: Role saved content: application/json: schema: type: object properties: role: $ref: '#/components/schemas/RoleDefinition' required: - role '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '503': $ref: '#/components/responses/ServiceUnavailable' delete: operationId: deleteRole summary: Delete a custom RBAC role tags: - Auth responses: '200': description: Role deleted content: application/json: schema: type: object properties: deleted: type: boolean name: type: string required: - deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/workers/{id}: parameters: - $ref: '#/components/parameters/WorkerID' - $ref: '#/components/parameters/TenantID' get: operationId: getWorker summary: Get a single worker runtime snapshot tags: - Workers responses: '200': description: Worker detail content: application/json: schema: $ref: '#/components/schemas/WorkerRuntime' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /api/v1/workers/{id}/jobs: parameters: - $ref: '#/components/parameters/WorkerID' - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/LimitQ' get: operationId: getWorkerJobs summary: List jobs associated with a worker tags: - Workers responses: '200': description: Jobs for worker content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/JobRecord' required: - items '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/workers/credentials: get: operationId: listWorkerCredentials summary: List external worker credentials tags: - WorkerCredentials parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Credential inventory content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/WorkerCredential' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' post: operationId: createWorkerCredential summary: Create or rotate a worker credential tags: - WorkerCredentials parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: worker_id: type: string allowed_pools: type: array items: type: string allowed_topics: type: array items: type: string required: - worker_id responses: '200': description: Credential rotated content: application/json: schema: $ref: '#/components/schemas/WorkerCredentialIssue' '201': description: Credential created content: application/json: schema: $ref: '#/components/schemas/WorkerCredentialIssue' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/workers/credentials/{worker_id}: parameters: - $ref: '#/components/parameters/WorkerCredentialID' - $ref: '#/components/parameters/TenantID' delete: operationId: deleteWorkerCredential summary: Revoke a worker credential tags: - WorkerCredentials responses: '204': description: Credential revoked '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/pools: get: operationId: listPools summary: List worker pools tags: - Pools parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Pool runtime inventory content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/PoolListItem' required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/pools/{name}: parameters: - $ref: '#/components/parameters/PoolName' - $ref: '#/components/parameters/TenantID' get: operationId: getPool summary: Get pool detail tags: - Pools responses: '200': description: Pool detail content: application/json: schema: $ref: '#/components/schemas/PoolDetail' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' put: operationId: createPool summary: Create a worker pool tags: - Pools requestBody: required: true content: application/json: schema: type: object properties: requires: type: array items: type: string description: type: string responses: '201': description: Pool created content: application/json: schema: $ref: '#/components/schemas/PoolMutation' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' patch: operationId: updatePool summary: Update a worker pool tags: - Pools requestBody: required: true content: application/json: schema: type: object properties: requires: type: array items: type: string description: type: string status: type: string responses: '200': description: Pool updated content: application/json: schema: $ref: '#/components/schemas/PoolMutation' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' delete: operationId: deletePool summary: Delete a worker pool tags: - Pools parameters: - $ref: '#/components/parameters/PoolName' - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/ForceQ' responses: '204': description: Pool deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/pools/{name}/drain: parameters: - $ref: '#/components/parameters/PoolName' - $ref: '#/components/parameters/TenantID' post: operationId: drainPool summary: Mark a pool as draining tags: - Pools requestBody: required: false content: application/json: schema: type: object properties: timeout_seconds: type: integer minimum: 1 responses: '200': description: Pool drain started content: application/json: schema: $ref: '#/components/schemas/PoolMutation' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/pools/{name}/topics/{topic}: parameters: - $ref: '#/components/parameters/PoolName' - $ref: '#/components/parameters/TopicPath' - $ref: '#/components/parameters/TenantID' put: operationId: addTopicToPool summary: Add a topic mapping to a pool tags: - Pools responses: '204': description: Topic mapped to pool '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' delete: operationId: removeTopicFromPool summary: Remove a topic mapping from a pool tags: - Pools responses: '204': description: Topic mapping removed '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' /api/v1/topics: get: operationId: listTopics summary: List topics tags: - Topics parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Topic registry content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/TopicResponse' registry_empty: type: boolean required: - items '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' post: operationId: createTopic summary: Create or replace a topic registration tags: - Topics parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: name: type: string pool: type: string input_schema_id: type: string output_schema_id: type: string pack_id: type: string requires: type: array items: type: string risk_tags: type: array items: type: string status: type: string required: - name responses: '200': description: Topic replaced content: application/json: schema: $ref: '#/components/schemas/TopicResponse' '201': description: Topic created content: application/json: schema: $ref: '#/components/schemas/TopicResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/topics/{name}: parameters: - $ref: '#/components/parameters/TopicName' - $ref: '#/components/parameters/TenantID' delete: operationId: deleteTopic summary: Delete a topic registration tags: - Topics responses: '204': description: Topic deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/license: get: operationId: getLicense summary: Get resolved license metadata tags: - License parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: License metadata content: application/json: schema: type: object properties: plan: type: string entitlements: $ref: '#/components/schemas/GenericObject' rights: $ref: '#/components/schemas/GenericObject' license: $ref: '#/components/schemas/LicenseInfo' expiry_status: type: string required: - plan - entitlements - rights '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/license/usage: get: operationId: getLicenseUsage summary: Get entitlement usage vs limits tags: - License parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Current license usage content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/license/reload: post: operationId: reloadLicense summary: Reload the active license tags: - License parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: License reloaded content: application/json: schema: type: object properties: status: type: string plan: type: string license: $ref: '#/components/schemas/LicenseInfo' required: - status - plan '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/telemetry/status: get: operationId: getTelemetryStatus summary: Telemetry status tags: - Telemetry parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: type: object properties: mode: type: string endpoint: type: string last_collected_at: type: string format: date-time last_reported_at: type: string format: date-time required: - mode '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/telemetry/inspect: get: operationId: inspectTelemetryPayload summary: Telemetry inspect tags: - Telemetry parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/telemetry/export: get: operationId: exportTelemetryPayload summary: Telemetry export tags: - Telemetry parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/telemetry/usage: get: operationId: getTelemetryUsage summary: Telemetry usage tags: - Telemetry parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/telemetry/consent: post: operationId: setTelemetryConsent summary: Set telemetry mode tags: - Telemetry parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: mode: type: string enum: - 'off' - local_only - anonymous required: - mode responses: '200': description: Telemetry mode updated content: application/json: schema: type: object properties: status: type: string mode: type: string required: - status - mode '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/admin/locks: get: operationId: listAdminLocks summary: List active distributed locks tags: - Locks parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Active lock entries content: application/json: schema: type: object properties: locks: type: array items: $ref: '#/components/schemas/AdminLock' required: - locks '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/audit/verify: get: operationId: verifyAuditChain summary: Verify audit chain integrity description: | Walks the tenant's audit stream and attests integrity of the hash chain. Reports `status=ok` on a contiguous chain, `status=gap` with classification when seq numbers are missing (retention-trimmed vs suspected tampering), and `status=tamper` on a broken hash link. Admin-only and entitlement-gated; NEVER returns raw event bodies — this is an integrity report surface, not event retrieval. tags: - AuditExport parameters: - $ref: '#/components/parameters/TenantID' - name: tenant in: query description: Tenant id (must match caller scope) schema: type: string - name: since in: query description: Inclusive lower bound on event time (unix ms) schema: type: integer format: int64 - name: until in: query description: Inclusive upper bound on event time (unix ms) schema: type: integer format: int64 - name: limit in: query description: Max events to read (default 10000, max 100000) schema: type: integer format: int32 responses: '200': description: Chain integrity report content: application/json: schema: $ref: '#/components/schemas/GenericObject' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' '503': description: Audit chainer not installed; integrity cannot be attested content: application/json: schema: $ref: '#/components/schemas/Error' /api/v1/audit/export: get: operationId: exportAuditCompliance summary: Stream compliance audit export description: | Streams a compliance-shaped audit export (ndjson or csv) for a bounded time window. Admin-only; gated by the `siem_export` or `audit_export` entitlement. The response is retention-aware: windows older than the configured retention + skew are rejected. The body carries SOC2 mapping columns when the operator has configured a mapping table. tags: - AuditExport parameters: - $ref: '#/components/parameters/TenantID' - name: from in: query description: Inclusive lower bound (RFC3339 timestamp) schema: type: string format: date-time - name: to in: query description: Exclusive upper bound (RFC3339 timestamp) schema: type: string format: date-time - name: format in: query description: Output format schema: type: string enum: - ndjson - csv default: ndjson - name: max_events in: query description: Cap on events emitted (clamped to entitlement ceiling) schema: type: integer format: int64 responses: '200': description: Streaming compliance export body (ndjson or csv) content: application/x-ndjson: schema: type: string text/csv: schema: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '500': $ref: '#/components/responses/InternalServerError' '503': description: Audit stream unavailable content: application/json: schema: $ref: '#/components/schemas/Error' /api/v1/audit/export/health: get: operationId: getAuditExportHealth summary: Audit export health tags: - AuditExport parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' /api/v1/audit/export/config: get: operationId: getAuditExportConfig summary: Audit export config tags: - AuditExport parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/audit/export/test: post: operationId: testAuditExport summary: Audit export test tags: - AuditExport parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' /api/v1/audit/legal-hold: post: operationId: createLegalHold summary: Create a legal hold tags: - LegalHold parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object properties: tenant_id: type: string reason: type: string required: - reason responses: '201': description: Legal hold created content: application/json: schema: $ref: '#/components/schemas/GenericObject' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '409': $ref: '#/components/responses/Conflict' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/audit/legal-holds: get: operationId: listLegalHolds summary: List legal holds tags: - LegalHold parameters: - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/TenantQ' responses: '200': description: Legal hold inventory content: application/json: schema: $ref: '#/components/schemas/GenericObject' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/audit/legal-hold/{id}: parameters: - $ref: '#/components/parameters/HoldID' - $ref: '#/components/parameters/TenantID' delete: operationId: releaseLegalHold summary: Release a legal hold tags: - LegalHold responses: '200': description: Legal hold released content: application/json: schema: $ref: '#/components/schemas/GenericObject' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/approvals/{job_id}/repair: post: operationId: repairApproval summary: Inspect or apply approval repair tags: - Approvals parameters: - name: job_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: false content: application/json: schema: type: object properties: apply: type: boolean note: type: string responses: '200': description: Repair inspection or successful repair content: application/json: schema: $ref: '#/components/schemas/GenericObject' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': description: Repair conflict content: application/json: schema: $ref: '#/components/schemas/GenericObject' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/output/stats: get: operationId: getOutputPolicyStats summary: Get aggregate output policy statistics tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' - $ref: '#/components/parameters/LimitQ' responses: '200': description: Output policy statistics content: application/json: schema: type: object properties: total_checks_24h: type: integer format: int64 quarantined_24h: type: integer format: int64 avg_latency_ms: type: integer format: int64 last_check_at: type: string format: date-time required: - total_checks_24h - quarantined_24h - avg_latency_ms - last_check_at '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/velocity-rules: get: operationId: listVelocityRules summary: List velocity rules tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Velocity rule inventory content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/VelocityRule' count: type: integer limit: type: integer format: int64 updated_at: type: string format: date-time upgrade_url: type: string errors: type: array items: $ref: '#/components/schemas/GenericObject' required: - items - count - limit '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' post: operationId: createVelocityRule summary: Create a velocity rule tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VelocityRule' responses: '201': description: Velocity rule created content: application/json: schema: $ref: '#/components/schemas/VelocityRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/velocity-rules/stats: get: operationId: getVelocityRuleStats summary: Get velocity rule statistics tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' responses: '200': description: Velocity rule stats content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/VelocityStats' top_rules: type: array items: $ref: '#/components/schemas/VelocityStats' generated_at: type: string format: date-time errors: type: array items: $ref: '#/components/schemas/GenericObject' required: - items - top_rules - generated_at '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/velocity-rules/{id}: parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' put: operationId: updateVelocityRule summary: Update a velocity rule tags: - Policy requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VelocityRule' responses: '200': description: Velocity rule updated content: application/json: schema: $ref: '#/components/schemas/VelocityRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteVelocityRule summary: Delete a velocity rule tags: - Policy responses: '204': description: Velocity rule deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierLimit' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/analytics: post: operationId: policyAnalytics summary: Compute per-rule quality analytics (false positive rates, approval fatigue) description: > Analyzes policy rule quality over a time range. Returns per-rule metrics including hit counts, override rates, approval latency, and daily trends. Read-only — no side effects. tags: - Policy security: - apiKey: [] requestBody: required: true content: application/json: schema: type: object properties: from: type: string format: date-time description: Start of analysis window (RFC3339) to: type: string format: date-time description: End of analysis window (RFC3339, max 7 days from 'from') rule_filter: type: string description: Optional rule ID to restrict analysis to a single rule required: - from - to responses: '200': description: Per-rule analytics content: application/json: schema: type: object properties: time_range: type: object properties: from: type: string format: date-time to: type: string format: date-time rules: type: array items: type: object properties: rule_id: type: string hit_count: type: integer approval_count: type: integer override_count: type: integer override_rate: type: number description: override_count / approval_count (0 if no approvals) avg_approval_latency_ms: type: integer format: int64 daily_hits: type: array items: type: integer description: Per-day hit counts (index 0 = oldest day) summary: type: object properties: total_rules: type: integer total_hits: type: integer total_overrides: type: integer highest_override_rule_id: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /api/v1/policy/replay: post: operationId: replayPolicyDecisions summary: Replay historical jobs against a candidate policy tags: - Policy parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PolicyReplayRequest' responses: '200': description: Replay result content: application/json: schema: $ref: '#/components/schemas/PolicyReplayResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/sse: get: operationId: mcpSSEV1 summary: MCP Server-Sent Events stream description: 'Opens an SSE connection to the MCP server. The server sends JSON-RPC notifications and responses over this channel. ' tags: - MCP security: - apiKey: [] - bearerAuth: [] responses: '200': description: SSE stream opened content: text/event-stream: schema: type: string '503': $ref: '#/components/responses/ServiceUnavailable' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /api/v1/mcp/message: post: operationId: mcpMessageV1 summary: Send a JSON-RPC 2.0 message to MCP server tags: - MCP security: - apiKey: [] - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/JsonRpcRequest' responses: '200': description: JSON-RPC response content: application/json: schema: $ref: '#/components/schemas/JsonRpcResponse' '400': $ref: '#/components/responses/BadRequest' '503': $ref: '#/components/responses/ServiceUnavailable' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /api/v1/agents: get: operationId: listAgents summary: List agent identities tags: - Agents parameters: - $ref: '#/components/parameters/TenantID' - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 50 - name: status in: query required: false schema: type: string - name: risk_tier in: query required: false schema: type: string - name: team in: query required: false schema: type: string responses: '200': description: Agent identity page content: application/json: schema: type: object properties: items: type: array items: type: object properties: id: {type: string} name: {type: string} description: {type: string} owner: {type: string} team: {type: string} risk_tier: {type: string} allowed_topics: type: array items: {type: string} allowed_pools: type: array items: {type: string} allowed_tools: type: array items: {type: string} data_classifications: type: array items: {type: string} status: {type: string} created_at: {type: string} updated_at: {type: string} last_active: type: integer format: int64 cursor: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' post: operationId: createAgent summary: Create an agent identity tags: - Agents parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object required: [name, owner, risk_tier] properties: name: {type: string} description: {type: string} owner: {type: string} team: {type: string} risk_tier: {type: string} allowed_topics: type: array items: {type: string} allowed_pools: type: array items: {type: string} allowed_tools: type: array items: {type: string} data_classifications: type: array items: {type: string} responses: '201': description: Agent identity created content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/{id}: parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' get: operationId: getAgent summary: Get one agent identity tags: - Agents responses: '200': description: Agent identity content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' put: operationId: updateAgent summary: Update an agent identity tags: - Agents requestBody: required: true content: application/json: schema: type: object properties: name: {type: string} description: {type: string} owner: {type: string} team: {type: string} risk_tier: {type: string} status: {type: string} allowed_topics: type: array items: {type: string} allowed_pools: type: array items: {type: string} allowed_tools: type: array items: {type: string} data_classifications: type: array items: {type: string} responses: '200': description: Agent identity updated content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' delete: operationId: deleteAgent summary: Delete an agent identity tags: - Agents responses: '204': description: Agent identity deleted '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/{id}/stats: get: operationId: getAgentStats summary: Get per-agent runtime statistics tags: - Agents parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Runtime statistics for the requested agent content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/{id}/delegate: post: operationId: issueDelegationToken summary: Issue a delegation token from one agent to another tags: - Agents - Delegation parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object required: [target_agent_id] properties: target_agent_id: {type: string} allowed_actions: type: array items: {type: string} allowed_topics: type: array items: {type: string} ttl_seconds: type: integer format: int64 parent_token: type: string responses: '201': description: Delegation token issued content: application/json: schema: type: object properties: token: {type: string} kid: {type: string} expires_at: {type: string} chain_depth: type: integer jti: {type: string} '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '429': $ref: '#/components/responses/RateLimited' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/verify-delegation: post: operationId: verifyDelegationToken summary: Verify a delegation token for an expected audience tags: - Agents - Delegation parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object required: [token] properties: token: {type: string} expected_audience: {type: string} responses: '200': description: Delegation verification result content: application/json: schema: type: object properties: valid: type: boolean sub: {type: string} aud: {type: string} allowed_actions: type: array items: {type: string} allowed_topics: type: array items: {type: string} chain_depth: type: integer delegation_chain: type: array items: $ref: '#/components/schemas/DelegationChainLink' error_code: {type: string} '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/revoke-delegation: post: operationId: revokeDelegationToken summary: Revoke a delegation token by JTI description: By default revoking a token cascades to every downstream delegation that extended it. Set `cascade=false` for narrow-scoped revocation. tags: - Agents - Delegation parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object required: [jti] properties: jti: {type: string} reason: {type: string} cascade: type: boolean default: true description: When true, revoke downstream delegations that extended this token. responses: '200': description: Delegation token revoked content: application/json: schema: type: object required: [jti, cascaded_count] properties: jti: type: string cascaded_count: type: integer description: Number of downstream delegations revoked in addition to the root token. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/{id}/delegations: get: operationId: listDelegationsForAgent summary: List delegation tokens issued by a specific agent tags: - Agents - Delegation parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' - name: status in: query required: false schema: type: string enum: [active, revoked, expired, all] - name: scope in: query required: false schema: type: string - name: before_expiry in: query required: false schema: type: string format: date-time - name: since_issued in: query required: false schema: type: string format: date-time - name: until_issued in: query required: false schema: type: string format: date-time - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer responses: '200': description: Delegation token page content: application/json: schema: $ref: '#/components/schemas/DelegationListResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/delegations: get: operationId: listDelegations summary: List delegation tokens for the current tenant tags: - Agents - Delegation parameters: - $ref: '#/components/parameters/TenantID' - name: status in: query required: false schema: type: string enum: [active, revoked, expired, all] - name: scope in: query required: false schema: type: string - name: before_expiry in: query required: false schema: type: string format: date-time - name: since_issued in: query required: false schema: type: string format: date-time - name: until_issued in: query required: false schema: type: string format: date-time - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer responses: '200': description: Delegation token page content: application/json: schema: $ref: '#/components/schemas/DelegationListResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/governance/decisions: get: operationId: listGovernanceDecisions summary: Query the governance decision log tags: - Governance parameters: - $ref: '#/components/parameters/TenantID' - name: since in: query required: false schema: type: string - name: until in: query required: false schema: type: string - name: topic in: query required: false schema: type: string - name: rule_id in: query required: false schema: type: string - name: verdict in: query required: false schema: type: string - name: agent_id in: query required: false schema: type: string - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer responses: '200': description: Governance decision page content: application/json: schema: type: object properties: items: type: array items: type: object properties: job_id: {type: string} topic: {type: string} matched_rule: {type: string} verdict: {type: string} reason: {type: string} constraints: type: object additionalProperties: true approval_status: {type: string} approval_decision: {type: string} agent_id: {type: string} policy_version: {type: string} timestamp: {type: string} next_cursor: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/approvals: get: operationId: listMcpApprovals summary: List MCP tool-call approvals tags: - MCP parameters: - $ref: '#/components/parameters/TenantID' - name: status in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 200 default: 50 - name: cursor in: query required: false schema: type: string responses: '200': description: MCP approval page content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true next_cursor: type: integer format: uint64 '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/approvals/{id}: get: operationId: getMcpApproval summary: Get one MCP approval record tags: - MCP parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: MCP approval record content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/approvals/{id}/approve: post: operationId: approveMcpApproval summary: Approve an MCP tool-call approval request tags: - MCP parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' responses: '200': description: MCP approval resolved content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/approvals/{id}/reject: post: operationId: rejectMcpApproval summary: Reject an MCP tool-call approval request tags: - MCP parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' responses: '200': description: MCP approval resolved content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/verify-signature: post: operationId: verifyMcpSignature summary: Verify an outbound MCP signature (admin-gated) description: >- Verify an ECDSA signature over an MCP request against the trusted-key store. Admin-only. See docs/mcp/outbound-signing.md. tags: - MCP security: - apiKey: [] - bearerAuth: [] requestBody: required: true content: application/json: schema: type: object additionalProperties: true responses: '200': description: Verification verdict content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/outbound: get: operationId: listMcpOutbound summary: List outbound MCP signed-call events description: >- Walks the tenant's audit chain stream and returns observed outbound MCP calls, filtered by time range + subject. Admin-only. tags: - MCP security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: since in: query required: false schema: type: string format: date-time - name: until in: query required: false schema: type: string format: date-time - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 500 default: 100 - name: cursor in: query required: false schema: type: string responses: '200': description: Outbound MCP event page content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/usage: get: operationId: getMcpUsage summary: Outbound MCP usage buckets from the audit chain description: >- Walks the tenant's audit chain and buckets outbound MCP calls by subject, method, and tool for usage analytics. Admin-only. tags: - MCP security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: since in: query required: false schema: type: string format: date-time - name: until in: query required: false schema: type: string format: date-time - name: group_by in: query required: false schema: type: string enum: [subject, method, tool] default: subject responses: '200': description: Usage buckets content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/tools: get: operationId: listMcpTools summary: List MCP tools visible to an agent or the full catalogue description: >- Returns the MCP tool catalogue. Without an `agent_id` query parameter, returns the unfiltered admin view. With `agent_id`, returns the subset of tools the identity is entitled to call after applying the MCP risk- tier and data-classification filter. tags: - MCP security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: agent_id in: query required: false schema: type: string responses: '200': description: Tool list for the requested scope content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/agents/{id}/tools: get: operationId: getAgentToolVisibility summary: List MCP tools visible to a specific agent identity description: >- Returns the subset of the MCP tool catalogue this agent identity can call after applying the identity-aware filter (risk tier + data classification). A revoked or suspended identity returns an empty list with an advisory `note` field rather than failing. tags: - MCP security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: id in: path required: true schema: type: string responses: '200': description: Tool list filtered for the agent identity content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/agents/{id}/denied-events: get: operationId: getAgentDeniedEvents summary: Recent mcp_tool_denied events for an agent identity description: >- Returns up to 50 of the most recent `mcp_tool_denied` audit events for the identity from the gateway's in-memory ring. Feeds the dashboard "recent denials" panel without requiring a SIEM pipeline. tags: - MCP security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: id in: path required: true schema: type: string responses: '200': description: Most recent denial records for the agent content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/evals/datasets/from-incidents: post: operationId: createEvalDatasetFromIncidents summary: Derive an eval dataset from governance incidents tags: - Evals parameters: - $ref: '#/components/parameters/TenantID' - name: dry_run in: query required: false schema: type: boolean requestBody: required: true content: application/json: schema: type: object required: [name] properties: tenant: {type: string} since: {type: string} until: {type: string} topic: {type: string} rule_id: {type: string} verdicts: type: array items: {type: string} agent_id: {type: string} max_entries: type: integer name: {type: string} description: {type: string} dry_run: type: boolean responses: '200': description: Dry-run extraction result content: application/json: schema: type: object additionalProperties: true '201': description: Eval dataset created from incidents content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '429': $ref: '#/components/responses/RateLimited' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets: # The gateway registers an internal Go 1.22 catch-all at # `/api/v1/evals/datasets/` and dispatches to `/by-name/{name}`, # `/{id}`, `/{id}/run`, and `/{id}/runs` from there because ServeMux # rejects those sibling patterns as ambiguous. The public API surface # is the canonical collection path plus the concrete child paths below; # `x-subroute-dispatch` tells openapi-audit about the internal prefix # route without modeling an invalid trailing-slash OpenAPI path. x-subroute-dispatch: true get: operationId: listEvalDatasets summary: List eval datasets tags: - Evals parameters: - $ref: '#/components/parameters/TenantID' - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer - name: name_prefix in: query required: false schema: type: string - name: created_after in: query required: false schema: type: string - name: created_before in: query required: false schema: type: string responses: '200': description: Eval dataset page content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true next_cursor: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': $ref: '#/components/responses/ServiceUnavailable' post: operationId: createEvalDataset summary: Create a curated eval dataset tags: - Evals parameters: - $ref: '#/components/parameters/TenantID' requestBody: required: true content: application/json: schema: type: object required: [name, version, entries] properties: name: {type: string} version: type: integer description: {type: string} entries: type: array items: type: object properties: id: {type: string} input: type: object additionalProperties: true expected_decision: {type: string} rule_id: {type: string} metadata: type: object additionalProperties: type: string source: {type: string} source_ref: {type: string} notes: {type: string} responses: '201': description: Eval dataset created content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '413': $ref: '#/components/responses/PayloadTooLarge' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets/by-name/{name}: get: operationId: listEvalDatasetVersions summary: List versions for an eval dataset family tags: - Evals parameters: - name: name in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' responses: '200': description: Dataset versions content: application/json: schema: type: object properties: items: type: array items: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets/by-name/{name}/versions/{version}: get: operationId: getEvalDatasetByNameVersion summary: Get a dataset by name and version tags: - Evals parameters: - name: name in: path required: true schema: type: string - name: version in: path required: true schema: type: integer - $ref: '#/components/parameters/TenantID' responses: '200': description: Eval dataset content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets/{id}: parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' get: operationId: getEvalDataset summary: Get one eval dataset tags: - Evals responses: '200': description: Eval dataset content: application/json: schema: type: object additionalProperties: true '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' put: operationId: createEvalDatasetSuccessor summary: Create a successor version from an existing dataset tags: - Evals requestBody: required: true content: application/json: schema: type: object properties: version: type: integer description: type: string entries: type: array items: type: object additionalProperties: true responses: '200': description: Successor dataset created content: application/json: schema: type: object additionalProperties: true '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '413': $ref: '#/components/responses/PayloadTooLarge' '503': $ref: '#/components/responses/ServiceUnavailable' delete: operationId: deleteEvalDataset summary: Delete an eval dataset tags: - Evals parameters: - name: force in: query required: false schema: type: boolean responses: '204': description: Dataset deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets/{id}/run: post: operationId: runEvalDataset summary: Run an eval dataset against the active or candidate policy description: Synchronous for ≤500 entries (returns 200 with full result), asynchronous for larger datasets (returns 202 with run_id; poll GET /evals/runs/{runId}). tags: - Evals parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/EvalRunRequest' responses: '200': description: Run completed synchronously content: application/json: schema: $ref: '#/components/schemas/EvalRunResult' '202': description: Run accepted for asynchronous processing content: application/json: schema: $ref: '#/components/schemas/EvalRunAcceptedResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '429': $ref: '#/components/responses/RateLimited' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/datasets/{id}/runs: get: operationId: listEvalRuns summary: List historical runs for one eval dataset tags: - Evals parameters: - name: id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' - name: cursor in: query required: false schema: type: string - name: limit in: query required: false schema: type: integer - name: has_regression in: query required: false schema: type: boolean - name: since in: query required: false schema: type: string format: date-time - name: until in: query required: false schema: type: string format: date-time - name: min_score in: query required: false schema: type: number format: float responses: '200': description: Run history page content: application/json: schema: $ref: '#/components/schemas/EvalRunsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/evals/runs/{run_id}: parameters: - name: run_id in: path required: true schema: type: string - $ref: '#/components/parameters/TenantID' get: operationId: getEvalRun summary: Get one eval run tags: - Evals responses: '200': description: Completed run result content: application/json: schema: $ref: '#/components/schemas/EvalRunResult' '202': description: Run is still pending or running content: application/json: schema: $ref: '#/components/schemas/EvalRunAcceptedResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' delete: operationId: deleteEvalRun summary: Delete a historical eval run tags: - Evals parameters: - name: force in: query required: false schema: type: boolean responses: '204': description: Eval run deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '503': $ref: '#/components/responses/ServiceUnavailable' /api/v1/mcp/status: get: operationId: mcpStatusV1 summary: Get MCP server status tags: - MCP security: - apiKey: [] - bearerAuth: [] responses: '200': description: MCP status content: application/json: schema: $ref: '#/components/schemas/McpStatus' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' parameters: - $ref: '#/components/parameters/TenantID' /api/v1/governance/approvals/analytics: get: operationId: getApprovalAnalytics summary: Approval analytics (time-to-approve, auto vs manual, bottlenecks) description: | Aggregated approval KPIs for the requested window. Consumes the Policy Decision Log as the source of truth, pairs each require_approval verdict with its ApprovalRecord, and computes per-group breakdowns sorted with the slowest approvers first so bottlenecks surface at the top. Requires the `governance.read` permission. Responses are memoised per `(tenant, window, group_by, limit)` tuple for 30 seconds so dashboards can poll without thrashing the decision-log index. tags: [Governance] security: - apiKey: [] - bearerAuth: [] parameters: - $ref: '#/components/parameters/TenantID' - name: window in: query description: Shorthand for `since`/`until` (wins when supplied). One of `24h`, `7d`, `30d`. required: false schema: type: string enum: [24h, 7d, 30d] - name: since in: query description: Inclusive lower bound (RFC3339 or unix milliseconds). Defaults to 24h before `until`. required: false schema: type: string - name: until in: query description: Exclusive upper bound (RFC3339 or unix milliseconds). Defaults to now. required: false schema: type: string - name: group_by in: query description: Breakdown axis; `overall` returns only the window-wide summary. required: false schema: type: string enum: [overall, rule, agent, topic] default: overall - name: limit in: query description: Top-N rows per group (bounded at 50). Ignored when `group_by=overall`. required: false schema: type: integer minimum: 1 maximum: 50 default: 10 responses: '200': description: Approval analytics response content: application/json: schema: $ref: '#/components/schemas/ApprovalAnalyticsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '503': description: Decision log store unavailable content: application/json: schema: $ref: '#/components/schemas/Error' components: securitySchemes: apiKey: type: apiKey in: header name: X-API-Key description: API key issued by the Cordum admin. bearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token obtained from /api/v1/auth/login. parameters: TenantID: name: X-Tenant-ID in: header required: true description: Tenant isolation header (required on all protected routes). schema: type: string IdempotencyKey: name: Idempotency-Key in: header required: false description: Optional idempotency key for safe retries. schema: type: string format: uuid JobID: name: id in: path required: true description: Job ID schema: type: string WorkflowID: name: id in: path required: true description: Workflow ID schema: type: string RunID: name: id in: path required: true description: Workflow run ID schema: type: string UserID: name: id in: path required: true description: User ID schema: type: string RoleName: name: name in: path required: true schema: type: string WorkerID: name: id in: path required: true schema: type: string WorkerCredentialID: name: worker_id in: path required: true schema: type: string PoolName: name: name in: path required: true schema: type: string TopicName: name: name in: path required: true schema: type: string TopicPath: name: topic in: path required: true schema: type: string HoldID: name: id in: path required: true schema: type: string LimitQ: name: limit in: query required: false schema: type: integer minimum: 1 ForceQ: name: force in: query required: false schema: type: boolean default: false TenantQ: name: tenant in: query required: false schema: type: string responses: BadRequest: description: Bad request — invalid or missing parameters content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized — missing or invalid credentials content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Forbidden — insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' Conflict: description: Conflict — resource state conflict content: application/json: schema: $ref: '#/components/schemas/Error' PayloadTooLarge: description: Payload too large content: application/json: schema: $ref: '#/components/schemas/Error' RateLimited: description: Too many requests — rate limit exceeded headers: Retry-After: schema: type: integer description: Seconds until the rate limit resets content: application/json: schema: $ref: '#/components/schemas/Error' InternalServerError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' BadGateway: description: Bad gateway — upstream service error content: application/json: schema: $ref: '#/components/schemas/Error' ServiceUnavailable: description: Service unavailable content: application/json: schema: $ref: '#/components/schemas/Error' TierLimit: description: Forbidden by current license entitlements content: application/json: schema: $ref: '#/components/schemas/GenericObject' schemas: ApprovalAnalyticsResponse: type: object required: [window, summary] properties: window: type: object required: [since, until] properties: since: type: string format: date-time until: type: string format: date-time summary: $ref: '#/components/schemas/ApprovalAnalyticsSummary' groups: type: array items: $ref: '#/components/schemas/ApprovalAnalyticsGroup' ApprovalAnalyticsSummary: type: object required: [total, approved, rejected, expired, auto_resolved, manual_resolved] properties: total: type: integer description: Total decisions with verdict=require_approval in the window (includes still-pending). approved: type: integer rejected: type: integer expired: type: integer auto_resolved: type: integer description: Resolutions driven by lifecycle events (expire/invalidate/repair), not human decision. manual_resolved: type: integer description: Resolutions where a human approver entered approve or reject. avg_time_to_approve_seconds: type: number nullable: true description: Null when no approvals resolved in the window — distinguishes "no data" from "all resolved in 0 s". p50: type: number nullable: true p90: type: number nullable: true p99: type: number nullable: true ApprovalAnalyticsGroup: type: object required: [key, label, total, approved, rejected, expired, auto_count, manual_count] properties: key: type: string description: Group identifier (rule id, agent id, or topic string). label: type: string total: type: integer approved: type: integer rejected: type: integer expired: type: integer auto_count: type: integer manual_count: type: integer avg_ttar_seconds: type: number nullable: true p90_seconds: type: number nullable: true DelegationChainLink: type: object required: [agent_id, issued_at, expires_at, jti, issued_by] properties: agent_id: type: string issued_at: type: string format: date-time expires_at: type: string format: date-time jti: type: string parent_jti: type: string nullable: true issued_by: type: string DelegationView: type: object required: [jti, issuer, subject, audience, chain_depth, issued_at, expires_at, revoked] properties: jti: type: string issuer: type: string description: Root issuer for the delegation chain. subject: type: string description: Agent that issued the token. audience: type: string description: Target agent the token was minted for. allowed_actions: type: array items: type: string allowed_topics: type: array items: type: string chain: type: array items: $ref: '#/components/schemas/DelegationChainLink' chain_depth: type: integer issued_at: type: string format: date-time expires_at: type: string format: date-time revoked: type: boolean revoked_at: type: string format: date-time nullable: true revoked_reason: type: string nullable: true DelegationListResponse: type: object required: [items] properties: items: type: array items: $ref: '#/components/schemas/DelegationView' next_cursor: type: string nullable: true Error: type: object properties: error: type: string description: Human-readable error message status: type: integer description: HTTP status code required: - error - status PaginatedResponse: type: object properties: next_cursor: type: string nullable: true description: Cursor for the next page (null if no more pages) AuthConfig: type: object properties: password_enabled: type: boolean user_auth_enabled: type: boolean saml_enabled: type: boolean saml_enterprise: type: boolean saml_login_url: type: string saml_metadata_url: type: string oidc_enabled: type: boolean oidc_issuer: type: string oidc_login_url: type: string oidc_client_id: type: string oidc_redirect_uri: type: string oidc_scopes: type: array items: type: string oidc_client_secret_masked: type: string session_ttl: type: string require_rbac: type: boolean require_principal: type: boolean default_tenant: type: string LoginRequest: type: object properties: username: type: string password: type: string format: password tenant: type: string required: - username - password LoginResponse: type: object properties: token: type: string expires_at: type: string format: date-time user: $ref: '#/components/schemas/AuthUser' ChangePasswordRequest: type: object properties: current_password: type: string format: password new_password: type: string format: password required: - current_password - new_password AuthUser: type: object properties: id: type: string username: type: string email: type: string format: email display_name: type: string tenant: type: string roles: type: array items: type: string source: type: string description: Auth source (local, saml, oidc) created_at: type: string format: date-time updated_at: type: string format: date-time last_login_at: type: string format: date-time nullable: true CreateUserRequest: type: object properties: username: type: string email: type: string format: email password: type: string format: password tenant: type: string role: type: string required: - username - password UpdateUserRequest: type: object properties: email: type: string format: email display_name: type: string roles: type: array items: type: string APIKeyInfo: type: object properties: id: type: string name: type: string prefix: type: string description: First characters of the key for identification scopes: type: array items: type: string createdAt: type: string format: date-time lastUsed: type: string format: date-time nullable: true usageCount: type: integer expiresAt: type: string format: date-time nullable: true CreateAPIKeyRequest: type: object properties: name: type: string scopes: type: array items: type: string expiresAt: type: string format: date-time nullable: true required: - name CreateAPIKeyResponse: type: object properties: key: $ref: '#/components/schemas/APIKeyInfo' secret: type: string description: Raw API key secret (shown only once) SubmitJobRequest: type: object properties: prompt: type: string description: The job payload / prompt text topic: type: string default: job.default description: Routing topic for worker dispatch adapter_id: type: string description: Adapter identifier for the target worker priority: type: string description: Job scheduling priority enum: - batch - critical - interactive default: interactive context: type: object additionalProperties: true description: Arbitrary context payload for the job memory_id: type: string description: Memory store identifier for context retrieval context_mode: type: string description: Context retrieval mode (e.g. full, summary) tenant_id: type: string description: Tenant identifier for multi-tenant isolation principal_id: type: string description: Principal (user/service) submitting the job actor_id: type: string description: Actor identifier for audit attribution actor_type: type: string description: Actor type (human, agent, system) idempotency_key: type: string description: Client-provided idempotency key to prevent duplicate jobs pack_id: type: string description: Integration pack identifier capability: type: string description: Required worker capability for dispatch risk_tags: type: array items: type: string description: Risk classification tags for policy evaluation requires: type: array items: type: string description: Required worker capabilities for dispatch matching org_id: type: string description: Organization identifier for hierarchical tenancy team_id: type: string description: Team identifier within the organization project_id: type: string description: Project identifier for scoped job tracking labels: type: object additionalProperties: type: string description: Arbitrary key-value labels for filtering and blast radius max_input_tokens: type: integer format: int32 description: Maximum input token budget for the job allow_summarization: type: boolean description: Whether the worker may summarize oversized input allow_retrieval: type: boolean description: Whether the worker may retrieve external context tags: type: array items: type: string description: Searchable tags for job discovery max_output_tokens: type: integer format: int64 description: Maximum output token budget max_total_tokens: type: integer format: int64 description: Maximum total (input + output) token budget deadline_ms: type: integer format: int64 description: Job deadline in milliseconds from submission (0 = no deadline) required: - prompt - topic SubmitJobResponse: type: object properties: job_id: type: string trace_id: type: string JobSummary: type: object properties: id: type: string state: type: string topic: type: string tenant: type: string updated_at: type: string format: date-time JobDetail: allOf: - $ref: '#/components/schemas/JobSummary' - type: object properties: trace_id: type: string prompt: type: string context_ptr: type: string nullable: true result_ptr: type: string nullable: true result: type: object additionalProperties: true nullable: true capability: type: string risk_tags: type: array items: type: string labels: type: object additionalProperties: type: string adapter_id: type: string priority: type: integer created_at: type: string format: date-time started_at: type: string format: date-time nullable: true completed_at: type: string format: date-time nullable: true error: type: string nullable: true retry_count: type: integer decisions: type: array items: $ref: '#/components/schemas/SafetyDecision' nullable: true delegation: $ref: '#/components/schemas/DelegationLineageView' DelegationLineageChainLink: type: object properties: agent_id: type: string issued_at: type: string format: date-time expires_at: type: string format: date-time jti: type: string parent_jti: type: string DelegationLineageView: type: object properties: jti: type: string audience: type: string root_issuer: type: string parent_issuer: type: string chain_depth: type: integer chain: type: array items: $ref: '#/components/schemas/DelegationLineageChainLink' scope: type: array items: type: string expires_at: type: string format: date-time verified_at: type: integer format: int64 reverified_at_dispatch: type: boolean SafetyDecision: type: object description: Result of safety kernel policy evaluation for a job properties: rule_id: type: string description: ID of the matched policy rule action: type: string description: Safety decision outcome enum: - ALLOW - DENY - REQUIRE_APPROVAL - THROTTLE - ALLOW_WITH_CONSTRAINTS - UNAVAILABLE reason: type: string description: Human-readable explanation for the decision constraints: type: object additionalProperties: true nullable: true description: Policy constraints applied if action is ALLOW_WITH_CONSTRAINTS evaluated_at: type: string format: date-time description: Timestamp of the policy evaluation DLQEntry: type: object properties: job_id: type: string topic: type: string tenant: type: string error: type: string failed_at: type: string format: date-time retry_count: type: integer original_state: type: string WorkflowDefinition: type: object properties: id: type: string org_id: type: string team_id: type: string name: type: string description: type: string version: type: string timeout_sec: type: integer steps: type: object description: Map of step ID to step definition additionalProperties: $ref: '#/components/schemas/WorkflowStep' config: type: object additionalProperties: true required: - id - name - steps WorkflowStep: type: object properties: id: type: string type: type: string description: Step type (job, switch, parallel, loop, transform, storage, subworkflow, approval) name: type: string depends_on: type: array items: type: string config: type: object additionalProperties: true timeout_sec: type: integer retry: type: object properties: max_attempts: type: integer backoff_sec: type: integer required: - id - type RunSummary: type: object properties: id: type: string workflow_id: type: string status: type: string description: Workflow run lifecycle status (lowercase) enum: - pending - running - waiting - succeeded - failed - denied - cancelled - timed_out started_at: type: string format: date-time completed_at: type: string format: date-time nullable: true error: type: object additionalProperties: true nullable: true description: Error details as key-value map (e.g. {code, message}) RunDetail: allOf: - $ref: '#/components/schemas/RunSummary' - type: object properties: input: type: object additionalProperties: true output: type: object additionalProperties: true nullable: true steps: type: object description: Map of step ID to step run status additionalProperties: $ref: '#/components/schemas/RunStepStatus' created_by: type: string dry_run: type: boolean RunStepStatus: type: object properties: step_id: type: string status: type: string started_at: type: string format: date-time nullable: true completed_at: type: string format: date-time nullable: true job_id: type: string nullable: true error: type: string nullable: true output: type: object additionalProperties: true nullable: true TimelineEvent: type: object properties: timestamp: type: string format: date-time type: type: string step_id: type: string nullable: true message: type: string data: type: object additionalProperties: true nullable: true ChatMessage: type: object properties: id: type: string run_id: type: string content: type: string role: type: string enum: - user - assistant - system step_id: type: string nullable: true job_id: type: string nullable: true created_at: type: string format: date-time PostChatRequest: type: object properties: content: type: string role: type: string enum: - user - assistant - system step_id: type: string job_id: type: string required: - content - role DryRunResult: type: object properties: steps: type: array items: $ref: '#/components/schemas/RunStepStatus' warnings: type: array items: type: string estimated_duration_sec: type: number ApprovalItem: type: object properties: job: $ref: '#/components/schemas/JobSummary' decision: type: string enum: - PENDING - APPROVED - REJECTED policy_snapshot: type: string nullable: true policy_rule_id: type: string nullable: true policy_reason: type: string nullable: true constraints: type: object additionalProperties: true nullable: true approval_required: type: boolean requested_at: type: string format: date-time decided_at: type: string format: date-time nullable: true decided_by: type: string nullable: true ApprovalDecisionRequest: type: object properties: reason: type: string note: type: string required: - reason PolicyCheckRequest: type: object properties: job_id: type: string topic: type: string tenant: type: string org_id: type: string team_id: type: string capability: type: string risk_tags: type: array items: type: string context: type: object additionalProperties: true labels: type: object additionalProperties: type: string PolicyCheckResponse: type: object properties: decision: type: string enum: - ALLOW - DENY - QUARANTINE - REQUIRE_APPROVAL reason: type: string rule_id: type: string nullable: true constraints: type: object additionalProperties: true nullable: true evaluations: type: array items: $ref: '#/components/schemas/SafetyDecision' nullable: true PolicySnapshot: type: object properties: id: type: string note: type: string author: type: string created_at: type: string format: date-time bundle_ids: type: array items: type: string nullable: true PolicyRule: type: object properties: id: type: string name: type: string description: type: string enabled: type: boolean action: type: string enum: - ALLOW - DENY - QUARANTINE - REQUIRE_APPROVAL conditions: type: object additionalProperties: true priority: type: integer source: type: string description: Bundle or file that defines this rule OutputRule: type: object properties: id: type: string name: type: string description: type: string enabled: type: boolean scanner_type: type: string pattern: type: string nullable: true action: type: string enum: - BLOCK - REDACT - WARN - LOG config: type: object additionalProperties: true nullable: true PolicyBundleSummary: type: object properties: id: type: string enabled: type: boolean source: type: string author: type: string message: type: string rule_count: type: integer updated_at: type: string format: date-time PolicyBundleDetail: allOf: - $ref: '#/components/schemas/PolicyBundleSummary' - type: object properties: content: type: string description: Raw YAML content of the bundle rules: type: array items: $ref: '#/components/schemas/PolicyRule' UpdatePolicyBundleRequest: type: object properties: content: type: string enabled: type: boolean author: type: string message: type: string PublishPolicyRequest: type: object properties: bundle_ids: type: array items: type: string author: type: string message: type: string note: type: string required: - bundle_ids - author RollbackPolicyRequest: type: object properties: snapshot_id: type: string author: type: string message: type: string note: type: string required: - snapshot_id - author PolicyAuditEntry: type: object properties: id: type: string action: type: string author: type: string timestamp: type: string format: date-time bundle_id: type: string nullable: true snapshot_id: type: string nullable: true message: type: string nullable: true ConfigDocument: type: object properties: scope: type: string enum: - system - org - team - workflow - step scope_id: type: string data: type: object additionalProperties: true description: Configuration key-value pairs SchemaRecord: type: object properties: id: type: string schema: type: object additionalProperties: true description: JSON Schema document required: - id - schema Lock: type: object properties: resource: type: string mode: type: string enum: - EXCLUSIVE - SHARED owners: type: array items: type: string updated_at: type: string format: date-time expires_at: type: string format: date-time LockRequest: type: object properties: resource: type: string owner: type: string mode: type: string enum: - EXCLUSIVE - SHARED ttl_ms: type: integer description: Lock time-to-live in milliseconds required: - resource - owner PackRecord: type: object properties: id: type: string name: type: string version: type: string status: type: string enum: - ACTIVE - INACTIVE - UNINSTALLED - ERROR description: type: string nullable: true author: type: string nullable: true installed_at: type: string format: date-time updated_at: type: string format: date-time verification: $ref: '#/components/schemas/InstalledPackVerification' description: | Server-verified pack signature state. Pre-existing installs from before signature verification shipped read as absent; clients SHOULD render an absent value as {signed: false}. InstalledPackVerification: type: object description: | Pack-signature verification outcome computed by the gateway at install time. Never client-supplied — the gateway discards any `verification` field on the install payload and computes its own. Pre-existing installs default to {signed: false} when this object is absent. required: [signed] properties: signed: type: boolean description: | True when the gateway successfully verified the pack against a trusted publisher key at install time. publisher_id: type: string description: Publisher identifier derived from the trusted key's metadata. kid: type: string description: Key id of the Ed25519 public key that verified the pack. verified_at: type: string format: date-time description: RFC3339 timestamp of the server-side verification. has_cordum_counter_sig: type: boolean description: | True when a valid Cordum counter-signature envelope (pack.yaml.sig.cordum) is present and verified against the embedded Cordum counter-signing key. signature_algorithm: type: string enum: [ed25519] description: Algorithm advertised by the verified envelope. Always ed25519 today. pack_signature_version: type: integer minimum: 1 description: Canonical manifest version that was signed. warnings: type: array description: | Non-fatal warnings the gateway wants to surface alongside the verification outcome (for example, "pack accepted unsigned — gateway strict mode disabled"). items: type: string PackVerification: type: object properties: pack_id: type: string valid: type: boolean checks: type: array items: type: object properties: name: type: string passed: type: boolean message: type: string MarketplaceCatalog: type: object properties: catalogs: type: array items: type: object properties: id: type: string name: type: string url: type: string items: type: array items: $ref: '#/components/schemas/MarketplacePack' fetched_at: type: string format: date-time cached: type: boolean MarketplacePack: type: object properties: catalog_id: type: string pack_id: type: string name: type: string version: type: string description: type: string author: type: string sha256: type: string url: type: string MarketplaceInstallRequest: type: object properties: catalog_id: type: string pack_id: type: string version: type: string url: type: string sha256: type: string force: type: boolean default: false upgrade: type: boolean default: false inactive: type: boolean default: false required: - catalog_id - pack_id - version StatusResponse: type: object properties: time: type: string format: date-time uptime_seconds: type: number build: type: object properties: version: type: string commit: type: string date: type: string nats: type: object properties: connected: type: boolean url: type: string redis: type: object properties: connected: type: boolean url: type: string workers: type: integer description: Number of connected workers license: type: object properties: valid: type: boolean tier: type: string nullable: true CreateArtifactRequest: type: object properties: content_base64: type: string format: byte content_type: type: string retention: type: string description: Retention policy (e.g., "30d", "permanent") labels: type: object additionalProperties: type: string required: - content_base64 - content_type CreateArtifactResponse: type: object properties: artifact_ptr: type: string size_bytes: type: integer ArtifactDetail: type: object properties: artifact_ptr: type: string content_base64: type: string format: byte metadata: type: object properties: content_type: type: string size_bytes: type: integer created_at: type: string format: date-time retention: type: string labels: type: object additionalProperties: type: string JsonRpcRequest: type: object properties: jsonrpc: type: string enum: - '2.0' id: oneOf: - type: string - type: integer method: type: string params: type: object additionalProperties: true required: - jsonrpc - method JsonRpcResponse: type: object properties: jsonrpc: type: string enum: - '2.0' id: oneOf: - type: string - type: integer result: type: object additionalProperties: true nullable: true error: type: object properties: code: type: integer message: type: string data: type: object additionalProperties: true nullable: true nullable: true McpStatus: type: object properties: running: type: boolean connected_clients: type: integer uptime_seconds: type: number transport: type: string enum: - sse - stdio GenericObject: type: object properties: {} additionalProperties: true RoleDefinition: type: object properties: name: type: string description: type: string permissions: type: array items: type: string inherits: type: array items: type: string built_in: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time required: - name RoleListResponse: type: object properties: roles: type: array items: $ref: '#/components/schemas/RoleDefinition' entitled: type: boolean required: - roles - entitled RoleDetailResponse: type: object properties: role: $ref: '#/components/schemas/RoleDefinition' resolved_permissions: type: array items: type: string required: - role - resolved_permissions RoleRequest: type: object properties: description: type: string permissions: type: array items: type: string inherits: type: array items: type: string WorkerRuntime: type: object properties: worker_id: type: string pool: type: string active_jobs: type: integer max_parallel_jobs: type: integer capabilities: type: array items: type: string cpu_load: type: number gpu_utilization: type: number memory_load: type: number region: type: string type: type: string labels: type: object properties: {} additionalProperties: type: string last_heartbeat: type: string format: date-time required: - worker_id JobRecord: type: object properties: id: type: string worker_id: type: string trace_id: type: string updated_at: type: integer format: int64 state: type: string topic: type: string tenant: type: string team: type: string principal: type: string actor_id: type: string actor_type: type: string idempotency_key: type: string capability: type: string risk_tags: type: array items: type: string requires: type: array items: type: string pack_id: type: string attempts: type: integer safety_decision: type: string safety_reason: type: string safety_rule_id: type: string safety_snapshot: type: string deadline_unix: type: integer format: int64 failure_reason: type: string required: - id - updated_at - state WorkerCredential: type: object properties: worker_id: type: string allowed_pools: type: array items: type: string allowed_topics: type: array items: type: string pack_id: type: string created_by: type: string created_at: type: string format: date-time revoked_at: type: string format: date-time required: - worker_id - created_by - created_at WorkerCredentialIssue: allOf: - $ref: '#/components/schemas/WorkerCredential' - type: object properties: token: type: string required: - token PoolListItem: type: object properties: name: type: string workers: type: integer active_jobs: type: integer capacity: type: integer utilization: type: number required: - name PoolDetail: type: object properties: name: type: string workers: type: array items: $ref: '#/components/schemas/WorkerRuntime' active_jobs: type: integer capacity: type: integer utilization: type: number topics: type: array items: type: string required: - name - workers - topics PoolMutation: type: object properties: name: type: string status: type: string requires: type: array items: type: string description: type: string drain_started_at: type: string format: date-time drain_timeout_seconds: type: integer required: - name - status TopicResponse: type: object properties: name: type: string pool: type: string input_schema_id: type: string output_schema_id: type: string pack_id: type: string requires: type: array items: type: string risk_tags: type: array items: type: string status: type: string active_worker_count: type: integer required: - name - status LicenseInfo: type: object properties: mode: type: string status: type: string plan: type: string org_id: type: string license_id: type: string deployment_type: type: string issued_at: type: string not_before: type: string expires_at: type: string features: type: array items: type: string limits: type: object properties: {} additionalProperties: type: integer format: int64 EvalRunRequest: type: object properties: use_current_policy: type: boolean candidate_bundle_id: type: string candidate_content: type: string max_entries: type: integer EvalRunAcceptedResponse: type: object properties: run_id: type: string format: uuid status: type: string enum: [pending, running, failed] poll_url: type: string dataset_id: type: string dataset_name: type: string dataset_version: type: integer tenant: type: string started_at: type: string format: date-time completed_at: type: string format: date-time policy_snapshot: type: string error: type: string required: - run_id - status EvalRunSummary: type: object properties: total: type: integer passed: type: integer failed: type: integer regressions: type: integer errored: type: integer score_percent: type: number format: float nullable: true required: - total - passed - failed - regressions - errored EvalEntryResult: type: object properties: entry_id: type: string input: type: object additionalProperties: true expected_decision: type: string actual_decision: type: string rule_id: type: string reason: type: string status: type: string enum: [pass, fail, regression, error] description: regression = expected was deny/require_approval/throttle/allow_with_constraints AND actual is allow drift_direction: type: string enum: [escalated, relaxed, unchanged] error: type: string required: - entry_id - status - drift_direction EvalRunResult: type: object properties: run_id: type: string format: uuid dataset_id: type: string dataset_name: type: string dataset_version: type: integer tenant: type: string policy_snapshot: type: string started_at: type: string format: date-time completed_at: type: string format: date-time summary: $ref: '#/components/schemas/EvalRunSummary' entries: type: array items: $ref: '#/components/schemas/EvalEntryResult' required: - run_id - dataset_id - dataset_name - dataset_version - tenant - policy_snapshot - started_at - completed_at - summary - entries EvalRunsResponse: type: object properties: items: type: array items: $ref: '#/components/schemas/EvalRunResult' next_cursor: type: string required: - items PolicyReplayRequest: type: object properties: from: type: string format: date-time to: type: string format: date-time filters: type: object properties: tenant: type: string topic_pattern: type: string original_decision: type: string candidate_bundle_id: type: string candidate_content: type: string use_current_policy: type: boolean max_jobs: type: integer required: - from - to PolicyReplayResponse: type: object properties: replay_id: type: string format: uuid policy_snapshot: type: string time_range: type: object properties: from: type: string format: date-time to: type: string format: date-time required: - from - to summary: type: object properties: total_jobs: type: integer evaluated: type: integer escalated: type: integer relaxed: type: integer unchanged: type: integer errored: type: integer rule_hits: type: array items: type: object properties: rule_id: type: string decision: type: string count: type: integer required: - rule_id - decision - count changes: type: array items: type: object properties: job_id: type: string topic: type: string tenant: type: string original_decision: type: string new_decision: type: string new_rule_id: type: string new_reason: type: string direction: type: string required: - job_id - topic - tenant - original_decision - new_decision - direction warnings: type: array items: type: string errors: type: array items: type: string required: - replay_id - policy_snapshot - time_range - summary - rule_hits - changes VelocityRule: type: object properties: id: type: string name: type: string match: type: object properties: topics: type: array items: type: string tenants: type: array items: type: string risk_tags: type: array items: type: string window: type: string key: type: string threshold: type: integer decision: type: string reason: type: string enabled: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - name VelocityStats: type: object properties: id: type: string hit_count_24h: type: integer format: int64 hit_rate_24h: type: number current_window_count: type: integer format: int64 current_window_max: type: integer format: int64 active_buckets: type: integer exceeded_buckets: type: integer last_triggered: type: string format: date-time hourly_hits: type: array items: type: integer format: int64 required: - id AdminLock: type: object properties: key: type: string holder: type: string ttl_remaining_ms: type: integer format: int64 type: type: string required: - key - holder - ttl_remaining_ms - type