{
  "openapi": "3.0.3",
  "info": {
    "title": "ZeroDrift Enforcement API",
    "version": "1.0.0",
    "description": "ZeroDrift Enforcement API"
  },
  "servers": [
    {
      "url": "https://api.zerodrift.ai"
    }
  ],
  "tags": [
    {
      "name": "Validate",
      "description": "Content enforcement and verdict polling"
    },
    {
      "name": "Custom Policies",
      "description": "Import policy documents, extract and manage custom rules"
    },
    {
      "name": "Training",
      "description": "Train and monitor customer-specific policy adapters"
    },
    {
      "name": "Activities",
      "description": "Eventually-consistent Command activity feed"
    },
    {
      "name": "Rule Packs",
      "description": "Managed and custom rule pack catalog"
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    }
  ],
  "paths": {
    "/api/v3/content/validate": {
      "post": {
        "summary": "Enforce Content",
        "operationId": "validateContent",
        "tags": [
          "Validate"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ItemValidateV3Email"
              },
              "example": {
                "content": "Guaranteed 15% returns, no risk. Act now \u2014 this opportunity will not last!",
                "model_engine": "anchor_3_0",
                "mode": "sync"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted (async) \u2014 poll the returned job URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidateAsyncAccepted"
                },
                "example": {
                  "api_version": "v3",
                  "job_id": "a1b2c3",
                  "status": "queued",
                  "mode": "async",
                  "submitted_at": "2026-09-23T04:44:13.879781+00:00",
                  "model_engine": "anchor_3_0",
                  "poll": {
                    "method": "GET",
                    "url": "/api/v3/jobs/a1b2c3"
                  }
                }
              }
            }
          },
          "200": {
            "description": "Completed (sync) \u2014 full v3 result envelope inline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResultResponse"
                },
                "example": {
                  "api_version": "v3",
                  "job_id": "a1b2c3",
                  "status": "completed",
                  "mode": "sync",
                  "submitted_at": "2026-09-23T04:44:13.879781+00:00",
                  "model_engine": "anchor_3_0",
                  "result": {
                    "overall_status": "needs_changes",
                    "summary": {
                      "do_not_send": 0,
                      "send_with_caution": 1,
                      "document_pages": 1
                    },
                    "revalidation_recommended": false,
                    "relevant_rule_ids": [
                      "guarantee_of_returns"
                    ],
                    "units": 1,
                    "cost": {
                      "currency": "USD",
                      "amount": "0.01"
                    },
                    "violations": [
                      {
                        "rule_id": "guarantee_of_returns",
                        "signal_name": "Guarantees of returns",
                        "rule_type": "SLM_SIGNAL",
                        "severity": "send_with_caution",
                        "confidence": "high",
                        "quote": "Guaranteed 15% returns, no risk.",
                        "fix": {
                          "action": "replace",
                          "suggested_text": "Returns are not guaranteed and involve risk."
                        }
                      }
                    ]
                  },
                  "extracted_text": null
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 invalid model_engine or unsupported parameters"
          },
          "403": {
            "description": "Forbidden \u2014 missing, invalid, inactive, or unresolved API key"
          },
          "422": {
            "description": "Unprocessable Entity"
          }
        }
      }
    },
    "/api/v3/jobs/{job_id}": {
      "get": {
        "summary": "Get Verdict",
        "operationId": "getValidationResult",
        "tags": [
          "Validate"
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "Job identifier returned by POST /api/v3/content/validate",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "V3 job envelope. Completed jobs use the same nested result shape as sync responses, with mode async.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResultResponse"
                },
                "example": {
                  "api_version": "v3",
                  "job_id": "a1b2c3",
                  "status": "completed",
                  "mode": "async",
                  "submitted_at": "2026-09-23T04:44:13.879781+00:00",
                  "model_engine": "anchor_3_0",
                  "result": {
                    "overall_status": "approved",
                    "summary": {
                      "do_not_send": 0,
                      "send_with_caution": 0,
                      "document_pages": 1
                    },
                    "revalidation_recommended": false,
                    "relevant_rule_ids": [],
                    "units": 1,
                    "cost": {
                      "currency": "USD",
                      "amount": "0.01"
                    },
                    "violations": []
                  },
                  "extracted_text": null
                }
              }
            }
          },
          "403": {
            "description": "Forbidden \u2014 missing, invalid, inactive, or unresolved API key"
          },
          "404": {
            "description": "Job Not Found"
          }
        }
      }
    },
    "/api/policies/import": {
      "post": {
        "summary": "Import Policy",
        "operationId": "importPolicy",
        "tags": [
          "Custom Policies"
        ],
        "description": "Import a policy document (inline base64 or plain text) and extract candidate rules asynchronously. Status transitions: processing \u2192 pending_review (rules extracted) | no_rules_found (nothing extractable) | failed (extraction error). Base64 payload size is limited by API Gateway/Lambda constraints \u2014 use the presigned-URL upload flow (Get Import Presigned URL \u2192 Start Import) for larger files.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyImportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Import Accepted \u2014 processing asynchronously",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyImportAccepted"
                },
                "example": {
                  "import_id": "550e8400-e29b-41d4-a716-446655440000",
                  "status": "processing",
                  "message": "Import accepted. Poll GET /api/policies/import/{import_id} for results."
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 missing or invalid fields"
          },
          "403": {
            "description": "Forbidden \u2014 invalid API key or inactive customer"
          },
          "422": {
            "description": "Unprocessable Entity \u2014 cannot extract text from document"
          }
        }
      },
      "get": {
        "summary": "List Policy Imports",
        "operationId": "listPolicyImports",
        "tags": [
          "Custom Policies"
        ],
        "responses": {
          "200": {
            "description": "List of imports for the authenticated customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyImportListResponse"
                },
                "example": {
                  "imports": [
                    {
                      "import_id": "550e8400-e29b-41d4-a716-446655440000",
                      "status": "pending_review",
                      "source_type": "file",
                      "origin": "uploaded",
                      "rule_count": 5,
                      "filename": "trading-policy.pdf",
                      "created_at": "2025-12-23T10:30:00Z",
                      "activated_at": null
                    },
                    {
                      "import_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
                      "status": "activated",
                      "source_type": "text",
                      "origin": "notion",
                      "rule_count": 12,
                      "filename": "Client Communications Policy",
                      "created_at": "2026-08-24T09:12:00Z",
                      "activated_at": "2026-08-24T09:31:00Z"
                    }
                  ],
                  "count": 2
                }
              }
            }
          },
          "403": {
            "description": "Forbidden \u2014 invalid API key or inactive customer"
          }
        }
      }
    },
    "/api/policies/import/presigned_url": {
      "post": {
        "summary": "Get Import Presigned URL",
        "operationId": "getPolicyImportPresignedUrl",
        "tags": [
          "Custom Policies"
        ],
        "description": "Step 1 of the large-file import flow. Returns an import_id and a presigned S3 PUT URL. Upload the raw policy file directly to S3 with the exact required_headers, then call Start Import with the import_id.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyImportPresignedUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned URL generated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyImportPresignedUrlResponse"
                },
                "example": {
                  "import_id": "550e8400-e29b-41d4-a716-446655440000",
                  "upload_url": "https://example-bucket.s3.amazonaws.com/policy-imports/raw/550e8400-e29b-41d4-a716-446655440000.bin?X-Amz-Algorithm=...",
                  "upload_method": "PUT",
                  "s3_bucket": "example-bucket",
                  "s3_key": "policy-imports/raw/550e8400-e29b-41d4-a716-446655440000.bin",
                  "required_headers": {
                    "Content-Type": "application/pdf"
                  },
                  "expires_in_seconds": 900,
                  "expires_at": "2026-06-15T10:45:00Z",
                  "max_upload_bytes": 1073741824,
                  "next_steps": {
                    "step_1": "PUT the raw file bytes to upload_url with the exact required_headers",
                    "step_2": "POST /api/policies/import/start with { \"import_id\": \"550e8400-...\" }"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 invalid JSON body"
          },
          "403": {
            "description": "Forbidden \u2014 invalid API key, inactive customer, or read_only API key (requires full_access)"
          },
          "500": {
            "description": "Internal Server Error \u2014 document storage misconfigured or presigned URL generation failed"
          }
        }
      }
    },
    "/api/policies/import/start": {
      "post": {
        "summary": "Start Import",
        "operationId": "startPolicyImport",
        "tags": [
          "Custom Policies"
        ],
        "description": "Step 3 of the large-file import flow. Verifies the uploaded S3 object exists and passed malware scanning, then triggers asynchronous rule extraction. Returns status: processing (extraction kicked off) or scan_pending (malware scan still running \u2014 no import record created yet; retry shortly).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyImportStartRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Import accepted or scan still pending",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyImportStartAccepted"
                },
                "examples": {
                  "processing": {
                    "summary": "Upload scanned clean, import kicked off",
                    "value": {
                      "import_id": "550e8400-e29b-41d4-a716-446655440000",
                      "status": "processing",
                      "message": "Import accepted. Poll GET /api/policies/import/{import_id} for results."
                    }
                  },
                  "scan_pending": {
                    "summary": "Malware scan still running \u2014 retry shortly",
                    "value": {
                      "import_id": "550e8400-e29b-41d4-a716-446655440000",
                      "status": "scan_pending",
                      "message": "File is still being scanned. Retry POST /api/policies/import/start shortly."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 missing import_id"
          },
          "403": {
            "description": "Forbidden \u2014 invalid API key, inactive customer, read_only key, or import belongs to another customer"
          },
          "404": {
            "description": "Not Found \u2014 no uploaded object for this import_id"
          },
          "409": {
            "description": "Conflict \u2014 import already started or completed"
          },
          "413": {
            "description": "Payload Too Large \u2014 uploaded file exceeds the maximum allowed size"
          },
          "422": {
            "description": "Unprocessable Entity \u2014 malware detected or file type not scannable"
          },
          "500": {
            "description": "Internal Server Error \u2014 do not retry on the same import_id"
          }
        }
      }
    },
    "/api/policies/import/{import_id}": {
      "get": {
        "summary": "Get Import Details",
        "operationId": "getPolicyImportDetails",
        "tags": [
          "Custom Policies"
        ],
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Import details including extracted rules",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyImportDetails"
                },
                "example": {
                  "import_id": "10f4ed8e-5240-4d87-8828-6e95f3e58a93",
                  "customer_id": "9a06364f-17c7-4a3f-9d98-61bde8696a2a",
                  "status": "pending_review",
                  "origin": "uploaded",
                  "source_type": "file",
                  "document_kind": "raw",
                  "filename": "Client_Communications_Policy.pdf",
                  "rule_count": 2,
                  "created_at": "2026-10-05T09:23:39.277706+00:00",
                  "extracted_rules": [
                    {
                      "id": "no_performance_guarantees",
                      "name": "No Performance Guarantees",
                      "type": "AI_SIGNAL",
                      "severity": "do_not_send",
                      "action": "flag",
                      "prompt": "Check if the document states or implies guaranteed returns, risk-free outcomes, or certainty of future investment performance. Return 1 if violation found, 0 if compliant.",
                      "source_text": "Employees must not state or imply guaranteed returns, risk-free outcomes, or certainty of future investment performance in any client communication.",
                      "keywords": [
                        "guaranteed returns",
                        "risk-free outcomes",
                        "certainty of future investment performance",
                        "can't lose"
                      ],
                      "window": "document",
                      "fix_note": "Remove any statements or implications of guaranteed returns or risk-free outcomes from the communication.",
                      "confidence": 0.95,
                      "regex_pattern": null,
                      "anchor": null,
                      "near": null,
                      "distance_chars": null,
                      "direction": null,
                      "case_insensitive": null,
                      "mode": null,
                      "min_hits": null,
                      "simulatable": true,
                      "activated_rule_id": null
                    },
                    {
                      "id": "risk_disclosure_present",
                      "name": "Risk Disclosure Near Performance Data",
                      "type": "REQUIRE_NEAR",
                      "severity": "send_with_caution",
                      "action": "flag",
                      "source_text": "Any presentation of past performance must appear alongside the standard risk disclosure.",
                      "keywords": [
                        "past performance",
                        "risk disclosure"
                      ],
                      "window": null,
                      "anchor": "past performance",
                      "near": [
                        "past performance is not indicative of future results"
                      ],
                      "distance_chars": 300,
                      "direction": "either",
                      "case_insensitive": true,
                      "mode": "document",
                      "min_hits": 1,
                      "fix_note": "Add the standard risk disclosure near the performance figures.",
                      "confidence": 0.92,
                      "regex_pattern": null,
                      "simulatable": true,
                      "activated_rule_id": null
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Import Not Found"
          }
        }
      }
    },
    "/api/policies/import/{import_id}/rules/{rule_id}": {
      "patch": {
        "summary": "Edit Imported Rule",
        "operationId": "editImportedRule",
        "tags": [
          "Custom Policies"
        ],
        "description": "Partially edit prompt, remediation guidance, or extraction confidence for one rule while its import is pending review. Explicit null clears prompt or fix_note. Requires a full_access API key.",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "description": "Extracted (unprefixed) rule ID within the import",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportedRuleUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated imported rule",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractedPolicyRule"
                }
              }
            }
          },
          "400": {
            "description": "Invalid imported rule update"
          },
          "403": {
            "description": "Forbidden \u2014 full_access API key required"
          },
          "404": {
            "description": "Import or imported rule not found"
          },
          "409": {
            "description": "Import is not pending review, or changed concurrently"
          }
        }
      }
    },
    "/api/policies/import/{import_id}/activate": {
      "post": {
        "summary": "Activate Rules",
        "operationId": "activateImportedRules",
        "tags": [
          "Custom Policies"
        ],
        "description": "Activate rules extracted from a pending policy import. If rule_ids is omitted, all extracted rules are activated.",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyActivateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rules activated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyActivateResponse"
                },
                "example": {
                  "import_id": "550e8400-e29b-41d4-a716-446655440000",
                  "status": "activated",
                  "activated_count": 5,
                  "activated_rule_ids": [
                    "cust_abc123_no_misleading_claims"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 no matching rule_ids"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Import Not Found"
          },
          "409": {
            "description": "Conflict \u2014 import already activated or rejected, or a conflicting rule_id (see conflicting_rule_id on the error body)"
          }
        }
      }
    },
    "/api/policies/import/{import_id}/train": {
      "post": {
        "summary": "Train Policy Adapter",
        "operationId": "trainPolicyAdapter",
        "tags": [
          "Training"
        ],
        "description": "Start asynchronous Training Studio training from the activated policy import's original document. Requires a full_access API key. Poll this same resource with GET until the run succeeds or fails.",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "description": "Activated policy import to train",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PolicyTrainingRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Training run accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyTrainingAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 invalid training options"
          },
          "403": {
            "description": "Forbidden \u2014 full_access API key required, or import belongs to another customer"
          },
          "404": {
            "description": "Import Not Found"
          },
          "409": {
            "description": "Conflict \u2014 import is not activated, or a training run is already in progress"
          },
          "410": {
            "description": "Gone \u2014 the original document expired under the retention policy; re-import it before training"
          },
          "422": {
            "description": "Unprocessable Entity \u2014 stored document is too short to train, or Training Studio rejected it"
          },
          "500": {
            "description": "Internal Server Error \u2014 import state could not be read or updated"
          },
          "502": {
            "description": "Bad Gateway \u2014 Training Studio request failed"
          },
          "503": {
            "description": "Service Unavailable \u2014 training is not configured in this environment"
          }
        }
      },
      "get": {
        "summary": "Get Training Status",
        "operationId": "getPolicyTrainingStatus",
        "tags": [
          "Training"
        ],
        "description": "Return the current Training Studio run for an activated policy import. When the run reaches succeeded, this poll promotes it to the import's active training_run_id so scoped validation can use the adapter.",
        "parameters": [
          {
            "name": "import_id",
            "in": "path",
            "required": true,
            "description": "Policy import whose training run to inspect",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current training run status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyTrainingStatus"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden \u2014 invalid API key or import belongs to another customer"
          },
          "404": {
            "description": "Import Not Found, or no training run has been started for it"
          },
          "500": {
            "description": "Internal Server Error \u2014 import state could not be read or updated"
          },
          "502": {
            "description": "Bad Gateway \u2014 Training Studio status request failed"
          },
          "503": {
            "description": "Service Unavailable \u2014 training is not configured in this environment"
          }
        }
      }
    },
    "/api/policies/rules": {
      "get": {
        "summary": "List Rules",
        "operationId": "listActiveRules",
        "tags": [
          "Custom Policies"
        ],
        "description": "List all rules for the authenticated customer with active/deactivated status: default rules (tagged with rule_pack_id and scenario IDs) and custom rules (tagged with import_id).",
        "responses": {
          "200": {
            "description": "Active rules list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActiveRulesListResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden \u2014 missing, invalid, or inactive API key"
          }
        }
      },
      "post": {
        "summary": "Create Custom Rule",
        "operationId": "createCustomRule",
        "tags": [
          "Custom Policies"
        ],
        "description": "Create a customer-owned AI_SIGNAL, REGEX, or REQUIRE_NEAR rule with a server-generated rule ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomRuleCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Custom rule created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomRuleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid custom rule definition"
          },
          "403": {
            "description": "Forbidden \u2014 full_access API key required"
          },
          "409": {
            "description": "Generated rule ID conflict"
          }
        }
      }
    },
    "/api/policies/rules/{rule_id}": {
      "get": {
        "summary": "Get Custom Rule",
        "operationId": "getCustomRule",
        "tags": [
          "Custom Policies"
        ],
        "description": "Return the full editable definition for a customer-owned custom rule.",
        "parameters": [
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Custom rule definition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomRuleResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Custom rule not found"
          }
        }
      },
      "patch": {
        "summary": "Edit Custom Rule",
        "operationId": "updateCustomRule",
        "tags": [
          "Custom Policies"
        ],
        "description": "Partially update an existing customer-owned rule. Rule type and metadata are immutable; supply only fields applicable to the stored rule type.",
        "parameters": [
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomRuleUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated custom rule",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomRuleResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid custom rule update"
          },
          "403": {
            "description": "Forbidden \u2014 full_access API key required"
          },
          "404": {
            "description": "Custom rule not found"
          },
          "409": {
            "description": "Concurrent custom rule update"
          }
        }
      },
      "delete": {
        "summary": "Delete Custom Rule",
        "operationId": "deleteCustomRule",
        "tags": [
          "Custom Policies"
        ],
        "description": "Permanently delete a customer-owned custom rule and remove its sole policy-import reference. Only cust_* definitions owned by the authenticated customer can be deleted; YAML/system pack rules are rejected. Deletion is rejected while the policy is pending review, training is in progress, a trained adapter still references the policy, or multiple policy imports reference the rule.",
        "parameters": [
          {
            "name": "rule_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Custom rule permanently deleted"
          },
          "400": {
            "description": "Rule is not a customer-owned cust_* definition"
          },
          "403": {
            "description": "Forbidden \u2014 full_access API key required"
          },
          "404": {
            "description": "Custom rule not found"
          },
          "409": {
            "description": "Rule has conflicting policy or training references, or changed concurrently"
          }
        }
      }
    },
    "/api/policies/rules/deactivate": {
      "post": {
        "summary": "Deactivate Rules",
        "operationId": "deactivateRules",
        "tags": [
          "Custom Policies"
        ],
        "description": "Deactivate rules by rule_pack_id, import_id, rule_pack_scenario_id, or rule_ids. Custom rules are soft-deactivated (preserved for audit); default rules are recorded as deactivated on the customer record. At least one selector field is required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RuleSelectorRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rules deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleDeactivateResponse"
                },
                "example": {
                  "deactivated_custom_count": 2,
                  "deactivated_custom_rule_ids": [
                    "cust_abc123_require_risk_disclosure",
                    "cust_abc123_past_performance_disclaimer"
                  ],
                  "deactivated_default_count": 47,
                  "deactivated_default_rule_ids": [
                    "signal_mnpi_leak",
                    "signal_promissory_tone"
                  ],
                  "errors": []
                }
              }
            }
          },
          "400": {
            "description": "Bad Request \u2014 at least one of rule_pack_id, import_id, rule_pack_scenario_id, or rule_ids is required"
          },
          "403": {
            "description": "Forbidden \u2014 import does not belong to customer"
          },
          "404": {
            "description": "Import not found"
          }
        }
      }
    },
    "/api/policies/rules/activate": {
      "post": {
        "summary": "Reactivate Rules",
        "operationId": "reactivateRules",
        "tags": [
          "Custom Policies"
        ],
        "description": "Re-activate previously deactivated rules by rule_pack_id, import_id, rule_pack_scenario_id, or rule_ids. For default rules, removes the rule from the customer's deactivated list; for custom rules, sets status back to active. At least one selector field is required. (Not to be confused with \"Activate Rules\", which activates rules freshly extracted from a policy import.)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RuleSelectorRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rules reactivated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleActivateResponse"
                },
                "example": {
                  "activated_custom_count": 3,
                  "activated_custom_rule_ids": [
                    "cust_abc123_require_risk_disclosure",
                    "cust_abc123_past_performance_disclaimer",
                    "cust_abc123_no_specific_client_returns"
                  ],
                  "activated_default_count": 0,
                  "activated_default_rule_ids": [],
                  "errors": []
                }
              }
            }
          },
          "400": {
            "description": "Bad request \u2014 invalid JSON body, malformed rule_ids, or no selectors supplied"
          },
          "403": {
            "description": "Forbidden \u2014 the supplied import_id belongs to a different customer"
          },
          "404": {
            "description": "Not Found \u2014 the supplied import_id does not exist (rule_pack_id / rule_pack_scenario_id failures are instead accumulated into the response's errors array with a 200)"
          }
        }
      }
    },
    "/api/activities": {
      "get": {
        "summary": "List Activity",
        "operationId": "listActivities",
        "tags": [
          "Activities"
        ],
        "description": "Newest-first, eventually-consistent Command activity feed. Date ranges are capped at 30 days.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "status",
            "in": "query",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "auto_fixed",
                  "blocked",
                  "warned",
                  "passed"
                ]
              }
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "source",
            "in": "query",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "rule_id",
            "in": "query",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activity feed page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActivityListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid date range (maximum 30 days)"
          },
          "401": {
            "description": "Invalid API key"
          }
        }
      }
    },
    "/api/activities/{activity_id}": {
      "get": {
        "summary": "Get Activity",
        "operationId": "getActivity",
        "tags": [
          "Activities"
        ],
        "parameters": [
          {
            "name": "activity_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full activity timeline and rule detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Activity"
                }
              }
            }
          },
          "404": {
            "description": "Not found, including cross-customer access"
          }
        }
      }
    },
    "/api/rulepacks/": {
      "get": {
        "summary": "Rule Packs",
        "operationId": "listRulePacks",
        "tags": [
          "Rule Packs"
        ],
        "description": "Returns the normalized policy catalog for the active authenticated customer. Managed entries reflect the Anchor 3.0 runtime vocabulary; custom entries are activated policy imports. Supplying id selects detail and requires type.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Rulepack ID for detail lookup; requires type",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter list results or disambiguate a detail ID",
            "schema": {
              "type": "string",
              "enum": [
                "managed",
                "custom"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Case-insensitive match against ID, name, or description",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated summaries for list requests, or one detailed rulepack",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RulepackCatalogResponse"
                },
                "examples": {
                  "list": {
                    "value": {
                      "data": [
                        {
                          "id": "zd-us-securities",
                          "name": "US Securities and Investment Communications",
                          "description": "SEC and FINRA communication compliance",
                          "type": "managed",
                          "engine": "anchor_3_0",
                          "active": true,
                          "total_rules": 72,
                          "active_rules": 72,
                          "inactive_rules": 0,
                          "publisher": "ZeroDrift",
                          "version": "3.0.0",
                          "domain": "securities",
                          "jurisdictions": [
                            "US"
                          ],
                          "last_updated": "2026-07-28"
                        }
                      ],
                      "meta": {
                        "current_page": 1,
                        "per_page": 15,
                        "total": 13,
                        "last_page": 1
                      }
                    }
                  },
                  "detail": {
                    "value": {
                      "data": {
                        "id": "zd-us-securities",
                        "name": "US Securities and Investment Communications",
                        "type": "managed",
                        "engine": "anchor_3_0",
                        "active": true,
                        "total_rules": 72,
                        "active_rules": 72,
                        "inactive_rules": 0,
                        "rules": [
                          {
                            "id": "guarantee_of_returns",
                            "name": "Promissory Tone",
                            "description": "Promissory words are risky",
                            "type": "AI_SIGNAL",
                            "severity": "major",
                            "verdict": "do_not_send",
                            "active": true,
                            "lifecycle": {
                              "status": "in_force",
                              "last_reviewed": "2026-07-28"
                            },
                            "citations": [
                              {
                                "framework": "FINRA Rule 2210",
                                "jurisdictions": [
                                  "US"
                                ]
                              }
                            ],
                            "remediation": {
                              "kind": "rewrite",
                              "note": "Review with compliance"
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid pagination, filter, or detail selector"
          },
          "403": {
            "description": "Forbidden \u2014 missing, invalid, inactive, or unresolved API key"
          },
          "404": {
            "description": "Rulepack not found or not accessible to this customer"
          },
          "500": {
            "description": "Catalog backend read failed or stored catalog data is inconsistent"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "name": "x-api-key",
        "in": "header",
        "description": "API key issued to your ZeroDrift customer account. Keys are either read_only or full_access; write/mutating endpoints require full_access."
      }
    },
    "schemas": {
      "ItemValidateV3Email": {
        "type": "object",
        "title": "ItemValidateV3Email",
        "description": "V3 content enforcement request. Supply the text to enforce as `content`. Requests also require `mode` and `model_engine`.",
        "required": [
          "content",
          "model_engine",
          "mode"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "Plain text to enforce."
          },
          "model_engine": {
            "type": "string",
            "enum": [
              "anchor_3_0_mini",
              "anchor_3_0",
              "anchor_3_0_max"
            ],
            "default": "anchor_3_0",
            "description": "Enforcement model. `anchor_3_0_mini` (Mini): detection-only yes/no plus a whole-message `flagged_rules` list \u2014 no per-line violations, rewrite, or `validation_scope`. `anchor_3_0` (Flagship, default): per-line violations plus rewrite; managed packs and trained adapters. With no `validation_scope`, Flagship stays on Flagship and checks the active managed catalog only; activated custom rules are not evaluated. `anchor_3_0_max` (Max): per-line violations, no rewrite; managed and custom rule text at inference. Ask for Max explicitly to enforce activated custom rules. A Flagship request that explicitly names a `cust_*` rule is upgraded to Max."
          },
          "mode": {
            "type": "string",
            "enum": [
              "async",
              "sync"
            ],
            "description": "Required. async returns 202 + job_id to poll; sync runs inline for short content and returns 200, falling back to async if it would exceed the inline budget."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional metadata merged into the job (document_type is forced to \"email\")"
          },
          "validation_scope": {
            "type": "object",
            "additionalProperties": false,
            "description": "Optional nested form for rules, rulepacks, imports, or `managed_only`. Resolve rule IDs from GET /api/policies/rules and rulepack IDs from GET /api/rulepacks/. On Flagship, omitting this object defaults to the active managed catalog so activated custom rules do not widen the request to Max. Naming a `cust_*` rule explicitly upgrades Flagship to Max.",
            "properties": {
              "rules": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "rulepacks": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "imports": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "managed_only": {
                "type": "boolean",
                "description": "Run every managed catalog rule this customer has switched on, and nothing else. Cannot be combined with rules, rulepacks, or imports. This is the best-effort default for a Flagship request that omits validation_scope; explicitly sending true fails with 422 if the managed set cannot be resolved."
              }
            }
          }
        }
      },
      "ValidateAsyncAccepted": {
        "type": "object",
        "title": "ValidateAsyncAccepted",
        "properties": {
          "api_version": {
            "type": "string",
            "enum": [
              "v3"
            ]
          },
          "job_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "async"
            ]
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time"
          },
          "model_engine": {
            "type": "string",
            "enum": [
              "anchor_3_0_mini",
              "anchor_3_0",
              "anchor_3_0_max"
            ],
            "description": "Requested engine after explicit custom-rule routing. A scope-less Flagship request remains `anchor_3_0`; naming a `cust_*` rule explicitly upgrades it to `anchor_3_0_max`."
          },
          "engine_upgrade_reason": {
            "type": "string",
            "enum": [
              "custom_rules"
            ],
            "description": "Present only when a Flagship request explicitly named custom rule text or a `cust_*` rule and was upgraded to Max."
          },
          "fallback_engine": {
            "type": "string",
            "description": "Engine selected when the requested engine could not serve the request."
          },
          "fallback_reason": {
            "type": "string"
          },
          "served_by_adapter": {
            "type": "string",
            "description": "Training Studio run ID when a trained adapter serves the request."
          },
          "applied_validation_scope": {
            "$ref": "#/components/schemas/AppliedValidationScope"
          },
          "poll": {
            "type": "object",
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "GET"
                ]
              },
              "url": {
                "type": "string"
              }
            }
          }
        }
      },
      "ValidationResultResponse": {
        "type": "object",
        "title": "ValidationResultResponse",
        "description": "V3 result envelope. Sync and polled async responses use the same shape; `mode` is the only execution-mode difference. Usage is reported as `result.units` and `result.cost`, not raw token counts.",
        "properties": {
          "api_version": {
            "type": "string",
            "enum": [
              "v3"
            ]
          },
          "job_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "completed",
              "failed"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "sync",
              "async"
            ],
            "description": "How the job ran. A sync request that fell through to the queue reports async."
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time"
          },
          "result": {
            "$ref": "#/components/schemas/ValidationVerdict"
          },
          "model_engine": {
            "type": "string",
            "enum": [
              "anchor_3_0_mini",
              "anchor_3_0",
              "anchor_3_0_max"
            ],
            "description": "Requested engine after explicit custom-rule routing. A scope-less Flagship request remains `anchor_3_0`."
          },
          "engine_upgrade_reason": {
            "type": "string",
            "enum": [
              "custom_rules"
            ],
            "description": "Present only when a Flagship request explicitly named custom rule text or a `cust_*` rule and was upgraded to Max."
          },
          "fallback_engine": {
            "type": "string",
            "description": "Engine that actually served the result after fallback."
          },
          "fallback_reason": {
            "type": "string"
          },
          "served_by_adapter": {
            "type": "string",
            "description": "Training Studio run ID when a trained adapter served the request."
          },
          "applied_validation_scope": {
            "$ref": "#/components/schemas/AppliedValidationScope"
          },
          "progress": {
            "type": "object",
            "properties": {
              "step": {
                "type": "string"
              }
            }
          },
          "extracted_text": {
            "type": "string",
            "nullable": true,
            "description": "Present when status is completed."
          },
          "error": {
            "type": "string",
            "description": "Present when status is failed."
          },
          "units": {
            "type": "integer",
            "description": "Present at the envelope top level when a failed job was billed."
          },
          "cost": {
            "$ref": "#/components/schemas/ValidationCost"
          }
        }
      },
      "AppliedValidationScope": {
        "type": "object",
        "title": "AppliedValidationScope",
        "description": "The enforced scope, reported as counts rather than the full rule ID list. A scope-less Flagship request is defaulted to the active managed catalog and therefore reports source `request`.",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "request",
              "policy_activation"
            ]
          },
          "rules_count": {
            "type": "integer"
          },
          "rulepacks_count": {
            "type": "integer"
          },
          "imports_count": {
            "type": "integer"
          }
        }
      },
      "ValidationCost": {
        "type": "object",
        "title": "ValidationCost",
        "properties": {
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "amount": {
            "type": "string",
            "description": "Decimal USD string."
          }
        }
      },
      "LineViolation": {
        "type": "object",
        "title": "LineViolation",
        "properties": {
          "line_number": {
            "type": "integer"
          },
          "line_text": {
            "type": "string"
          },
          "char_from": {
            "type": "integer"
          },
          "char_to": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "rule_count": {
            "type": "integer"
          },
          "rules_violated": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rule_id": {
                  "type": "string"
                },
                "rule_name": {
                  "type": "string"
                },
                "rule_ref": {
                  "type": "string",
                  "description": "Regulatory citation, e.g. FINRA 2210(d)(1)(B)"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "do_not_send",
                    "send_with_caution"
                  ]
                },
                "confidence": {
                  "type": "number",
                  "format": "float"
                },
                "action": {
                  "type": "string",
                  "enum": [
                    "replace",
                    "insert_after",
                    "remove",
                    "warning"
                  ]
                }
              }
            }
          },
          "best_fix": {
            "type": "object",
            "properties": {
              "severity": {
                "type": "string",
                "enum": [
                  "do_not_send",
                  "send_with_caution"
                ]
              },
              "rule_id": {
                "type": "string"
              },
              "rule_name": {
                "type": "string"
              },
              "rule_ref": {
                "type": "string"
              },
              "action": {
                "type": "string"
              },
              "suggested_text": {
                "type": "string",
                "nullable": true
              },
              "human_action": {
                "type": "string"
              },
              "confidence": {
                "type": "number",
                "format": "float"
              }
            }
          }
        }
      },
      "EmailViolation": {
        "type": "object",
        "title": "EmailViolation",
        "properties": {
          "rule_id": {
            "type": "string"
          },
          "signal_name": {
            "type": "string"
          },
          "rule_name": {
            "type": "string"
          },
          "rule_type": {
            "type": "string",
            "description": "Detector that raised the violation, for example SLM_SIGNAL, CUSTOM_RULE_TEXT, or TRAINED_ADAPTER."
          },
          "rule_pack_id": {
            "type": "string",
            "nullable": true
          },
          "rule_pack_name": {
            "type": "string",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "quote": {
            "type": "string"
          },
          "char_from": {
            "type": "integer"
          },
          "char_to": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "reasoning": {
            "type": "string",
            "description": "Anchor engines only. Why the line was flagged."
          },
          "verifier_note": {
            "type": "string",
            "description": "Anchor engines only."
          },
          "fix": {
            "type": "object",
            "properties": {
              "action": {
                "type": "string",
                "enum": [
                  "replace",
                  "insert_after",
                  "remove",
                  "warning",
                  "remove_or_escalate",
                  "none"
                ],
                "description": "remove_or_escalate (anchor_3_0 only): the line can't be rewritten compliantly while preserving its meaning. suggested_text is empty; remove the line or escalate for human review."
              },
              "suggested_text": {
                "type": "string",
                "description": "Suggested replacement text. Null or empty when `action` is `remove`/`warning`, or on Max jobs when the rule has no fix note \u2014 Max does not rewrite. On a Max job, when present this carries the rule's own fix note rather than a model rewrite."
              },
              "fix_note": {
                "type": "string"
              },
              "human_action": {
                "type": "string",
                "description": "Trained-adapter rewrites only."
              },
              "fix_source": {
                "type": "string",
                "enum": [
                  "rewrite_adapter",
                  "rewrite_adapter_sentinel",
                  "policy_suggested_text"
                ]
              }
            }
          }
        }
      },
      "V3FlaggedRule": {
        "type": "object",
        "title": "V3FlaggedRule",
        "description": "A whole-message verdict from Mini. Mini has no quote or line position.",
        "properties": {
          "rule_id": {
            "type": "string"
          },
          "signal_id": {
            "type": "string",
            "deprecated": true,
            "description": "Deprecated duplicate of rule_id."
          },
          "signal_name": {
            "type": "string"
          },
          "rule_type": {
            "type": "string",
            "enum": [
              "SLM_SIGNAL"
            ]
          },
          "rule_pack_id": {
            "type": "string",
            "nullable": true
          },
          "rule_pack_name": {
            "type": "string",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "p": {
            "type": "number",
            "description": "Probability behind the verdict."
          }
        }
      },
      "PolicyImportRequest": {
        "type": "object",
        "title": "PolicyImportRequest",
        "required": [
          "source",
          "content"
        ],
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "file",
              "text"
            ],
            "description": "Whether content is a base64-encoded file or plain text"
          },
          "content": {
            "type": "string",
            "description": "Base64-encoded document bytes (for file) or plain text (for text)"
          },
          "filename": {
            "type": "string",
            "description": "Original filename (optional, for file uploads)"
          }
        }
      },
      "PolicyImportAccepted": {
        "type": "object",
        "title": "PolicyImportAccepted",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "PolicyImportSummary": {
        "type": "object",
        "title": "PolicyImportSummary",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "pending_review",
              "no_rules_found",
              "failed",
              "activated",
              "partially_activated"
            ]
          },
          "source_type": {
            "type": "string",
            "enum": [
              "file",
              "text"
            ]
          },
          "origin": {
            "type": "string",
            "description": "Where the document came from, as one value: a connector's provider slug (notion, linear, atlassian, google_drive) when a connected workspace produced it, uploaded when it was staged through this API as a file, or built when it was authored in the caller's own UI. Deliberately not an enum -- a new connector adds a slug.",
            "example": "notion"
          },
          "rule_count": {
            "type": "integer"
          },
          "filename": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PolicyImportListResponse": {
        "type": "object",
        "title": "PolicyImportListResponse",
        "properties": {
          "imports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PolicyImportSummary"
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "PolicyImportPresignedUrlRequest": {
        "type": "object",
        "title": "PolicyImportPresignedUrlRequest",
        "description": "Request a presigned S3 PUT URL for uploading a large policy file. The returned upload_url and required_headers must be used verbatim, or S3 rejects the upload with SignatureDoesNotMatch.",
        "properties": {
          "filename": {
            "type": "string",
            "description": "Original filename, for reference/audit",
            "example": "my-compliance-policy.pdf"
          },
          "content_type": {
            "type": "string",
            "description": "MIME type of the file to be uploaded",
            "default": "application/pdf",
            "example": "application/pdf"
          }
        }
      },
      "PolicyImportPresignedUrlResponse": {
        "type": "object",
        "title": "PolicyImportPresignedUrlResponse",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "upload_url": {
            "type": "string"
          },
          "upload_method": {
            "type": "string",
            "enum": [
              "PUT"
            ]
          },
          "s3_bucket": {
            "type": "string"
          },
          "s3_key": {
            "type": "string"
          },
          "required_headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "expires_in_seconds": {
            "type": "integer",
            "default": 900
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "max_upload_bytes": {
            "type": "integer",
            "description": "Maximum file size (1 GB = 1073741824 bytes)"
          },
          "next_steps": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "PolicyImportStartRequest": {
        "type": "object",
        "title": "PolicyImportStartRequest",
        "required": [
          "import_id"
        ],
        "properties": {
          "import_id": {
            "type": "string",
            "description": "Import identifier returned from the presigned_url endpoint"
          },
          "filename": {
            "type": "string",
            "description": "Optional filename to record on the import (overrides the presigned-step value)"
          }
        }
      },
      "PolicyImportStartAccepted": {
        "type": "object",
        "title": "PolicyImportStartAccepted",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "scan_pending"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "PolicyImportDetails": {
        "type": "object",
        "title": "PolicyImportDetails",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "customer_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "pending_review",
              "no_rules_found",
              "failed",
              "activated",
              "partially_activated"
            ]
          },
          "origin": {
            "type": "string",
            "description": "Where the document came from, as one value: a connector's provider slug (notion, linear, atlassian, google_drive) when a connected workspace produced it, uploaded when it was staged through this API as a file, or built when it was authored in the caller's own UI. Deliberately not an enum -- a new connector adds a slug.",
            "example": "notion"
          },
          "source_type": {
            "type": "string",
            "enum": [
              "file",
              "text"
            ],
            "description": "How the content was submitted."
          },
          "document_kind": {
            "type": "string",
            "enum": [
              "raw",
              "text"
            ],
            "description": "How the stored document is read during extraction."
          },
          "filename": {
            "type": "string",
            "description": "Original filename, when the import was submitted with one."
          },
          "rule_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent activation."
          },
          "activated_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Customer-prefixed rule IDs activated from this import, sorted."
          },
          "error_message": {
            "type": "string",
            "description": "Error details when status is failed."
          },
          "extracted_rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExtractedPolicyRule"
            },
            "description": "Every rule carries the whole field set; fields that do not apply to the rule's type are returned as null."
          },
          "source_provider": {
            "type": "string",
            "description": "Connector provenance. Present only on imports a connected workspace produced; absent on documents uploaded through this API."
          },
          "source_connection_id": {
            "type": "string",
            "description": "The connection the document was fetched through."
          },
          "source_id": {
            "type": "string",
            "description": "The provider's own identifier for the source document."
          },
          "source_uri": {
            "type": "string",
            "description": "The document's URL at the provider, recorded verbatim."
          },
          "source_name": {
            "type": "string",
            "description": "The document's title at the provider, when it has one."
          },
          "source_author": {
            "type": "string",
            "description": "Who the provider says last edited the document."
          },
          "source_last_modified": {
            "type": "string",
            "description": "When the provider says it last changed, in the provider's own format."
          },
          "source_tool": {
            "type": "string",
            "description": "The MCP tool whose result carried the document."
          },
          "content_sha256": {
            "type": "string",
            "description": "Digest of the imported content as \"sha256:<hex>\". Re-importing content with this digest is a no-op."
          },
          "content_bytes": {
            "type": "integer",
            "description": "Size of the document the connector fetched, as the connector measured it."
          },
          "mime_type": {
            "type": "string",
            "description": "Media type the provider reported for the document, e.g. text/markdown."
          },
          "fetched_at": {
            "type": "string",
            "description": "When the connector fetched the document, ISO-8601 UTC."
          }
        }
      },
      "ImportedRuleUpdateRequest": {
        "type": "object",
        "title": "ImportedRuleUpdateRequest",
        "description": "Partial edit for one pending imported rule. prompt is accepted only for AI_SIGNAL rules. Explicit null clears prompt or fix_note.",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "prompt": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "ExtractedPolicyRule": {
        "type": "object",
        "title": "ExtractedPolicyRule",
        "required": [
          "id",
          "type",
          "name",
          "keywords",
          "simulatable",
          "activated_rule_id"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "AI_SIGNAL",
              "REGEX",
              "REQUIRE_NEAR"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ],
            "description": "Canonical extracted severity. Legacy low/medium/high values are normalized on read."
          },
          "action": {
            "type": "string",
            "enum": [
              "flag",
              "fix"
            ],
            "description": "Suggested action: flag (warn only) or fix (suggest replacement text)"
          },
          "source_text": {
            "type": "string",
            "description": "Source policy text retained as provenance"
          },
          "prompt": {
            "type": "string",
            "nullable": true,
            "minLength": 1,
            "description": "AI instruction, used only by AI_SIGNAL rules"
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present on every extracted rule. Used as a runtime gate only by AI_SIGNAL rules."
          },
          "window": {
            "type": "string",
            "enum": [
              "sentence",
              "paragraph",
              "document"
            ]
          },
          "regex_pattern": {
            "type": "string",
            "maxLength": 512
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "suggested_text": {
            "type": "string",
            "description": "Optional exact suggestion preserved by the import. Explicit null suppresses the fix_note fallback; when omitted, a warning rule may derive its suggestion from fix_note.",
            "nullable": true,
            "minLength": 1
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "AI confidence score (0-1)"
          },
          "simulatable": {
            "type": "boolean",
            "description": "Derived at read time; true when the pending rule can run against a text snippet."
          },
          "activated_rule_id": {
            "type": "string",
            "nullable": true,
            "description": "Derived at read time. Contains the customer-prefixed rule ID when this extracted rule has been activated; null while it remains pending, including pending siblings in a partially activated import."
          },
          "anchor": {
            "type": "string",
            "maxLength": 512
          },
          "near": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "maxLength": 512
            }
          },
          "distance_chars": {
            "type": "integer",
            "minimum": 0,
            "default": 100
          },
          "direction": {
            "type": "string",
            "enum": [
              "before",
              "after",
              "either"
            ],
            "default": "either"
          },
          "case_insensitive": {
            "type": "boolean",
            "default": false
          },
          "mode": {
            "type": "string",
            "enum": [
              "document",
              "page"
            ],
            "default": "document"
          },
          "min_hits": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          }
        }
      },
      "PolicyActivateRequest": {
        "type": "object",
        "title": "PolicyActivateRequest",
        "properties": {
          "rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional subset of extracted rule IDs to activate. If omitted, all rules are activated."
          },
          "overwrite": {
            "type": "boolean",
            "description": "Set true to overwrite rules already activated from a previous import. Defaults to false."
          }
        }
      },
      "PolicyActivateResponse": {
        "type": "object",
        "title": "PolicyActivateResponse",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "activated"
            ]
          },
          "activated_count": {
            "type": "integer"
          },
          "activated_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "conflicting_rule_id": {
            "type": "string",
            "description": "Present on 409 responses: the rule ID that caused the conflict"
          }
        }
      },
      "PolicyTrainingRequest": {
        "type": "object",
        "title": "PolicyTrainingRequest",
        "additionalProperties": false,
        "properties": {
          "cost_cap_usd": {
            "type": "number",
            "format": "float",
            "minimum": 0,
            "description": "Optional maximum Training Studio generation spend in US dollars"
          },
          "examples_per_rule": {
            "type": "integer",
            "minimum": 1,
            "description": "Optional number of generated training examples per extracted rule"
          }
        }
      },
      "PolicyTrainingAccepted": {
        "type": "object",
        "title": "PolicyTrainingAccepted",
        "required": [
          "import_id",
          "training_run_id",
          "status"
        ],
        "properties": {
          "import_id": {
            "type": "string"
          },
          "training_run_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Initial Training Studio run status"
          },
          "message": {
            "type": "string"
          },
          "poll": {
            "$ref": "#/components/schemas/PolicyTrainingPoll"
          }
        }
      },
      "PolicyTrainingStatus": {
        "type": "object",
        "title": "PolicyTrainingStatus",
        "required": [
          "import_id",
          "training_run_id",
          "status"
        ],
        "properties": {
          "import_id": {
            "type": "string"
          },
          "training_run_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Training Studio run status; succeeded and failed are terminal"
          },
          "stage": {
            "type": "string",
            "nullable": true,
            "description": "Current training pipeline stage, when reported"
          },
          "progress": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Training Studio progress details, when reported"
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Failure detail when status is failed"
          }
        }
      },
      "PolicyTrainingPoll": {
        "type": "object",
        "title": "PolicyTrainingPoll",
        "required": [
          "method",
          "url"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "GET"
            ]
          },
          "url": {
            "type": "string"
          }
        }
      },
      "RuleSelectorRequest": {
        "type": "object",
        "title": "RuleSelectorRequest",
        "minProperties": 1,
        "properties": {
          "rule_pack_id": {
            "type": "string",
            "description": "e.g. sec_finra_comms_v2"
          },
          "import_id": {
            "type": "string"
          },
          "rule_pack_scenario_id": {
            "type": "string",
            "description": "Rule-pack scenario selector"
          },
          "rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RuleDeactivateResponse": {
        "type": "object",
        "title": "RuleDeactivateResponse",
        "properties": {
          "deactivated_custom_count": {
            "type": "integer"
          },
          "deactivated_custom_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "deactivated_default_count": {
            "type": "integer"
          },
          "deactivated_default_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          }
        }
      },
      "RuleActivateResponse": {
        "type": "object",
        "title": "RuleActivateResponse",
        "properties": {
          "activated_custom_count": {
            "type": "integer"
          },
          "activated_custom_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "activated_default_count": {
            "type": "integer"
          },
          "activated_default_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          }
        }
      },
      "CustomRuleCreateRequest": {
        "title": "CustomRuleCreateRequest",
        "oneOf": [
          {
            "$ref": "#/components/schemas/CustomRuleCreateAiSignalRequest"
          },
          {
            "$ref": "#/components/schemas/CustomRuleCreateRegexRequest"
          },
          {
            "$ref": "#/components/schemas/CustomRuleCreateRequireNearRequest"
          }
        ]
      },
      "CustomRuleCreateAiSignalRequest": {
        "type": "object",
        "title": "CustomRuleCreateAiSignalRequest",
        "additionalProperties": false,
        "required": [
          "name",
          "type",
          "action",
          "severity",
          "prompt",
          "keywords"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "AI_SIGNAL"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1,
            "description": "Editable suggested replacement or warning guidance. Omit to allow warning fallback on create; null explicitly clears/suppresses it."
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Allowed only for AI_SIGNAL rules"
          },
          "keywords": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "Allowed only for AI_SIGNAL rules"
          },
          "window": {
            "type": "string",
            "enum": [
              "sentence",
              "paragraph",
              "document"
            ],
            "default": "document"
          }
        }
      },
      "CustomRuleCreateRegexRequest": {
        "type": "object",
        "title": "CustomRuleCreateRegexRequest",
        "additionalProperties": false,
        "required": [
          "name",
          "type",
          "action",
          "severity",
          "regex_pattern"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "REGEX"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "regex_pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Python regex, allowed only for REGEX rules"
          }
        }
      },
      "CustomRuleCreateRequireNearRequest": {
        "type": "object",
        "title": "CustomRuleCreateRequireNearRequest",
        "additionalProperties": false,
        "required": [
          "name",
          "type",
          "action",
          "severity",
          "anchor",
          "near"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "REQUIRE_NEAR"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "anchor": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Python regex anchor pattern"
          },
          "near": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          "distance_chars": {
            "type": "integer",
            "minimum": 0,
            "default": 100
          },
          "direction": {
            "type": "string",
            "enum": [
              "before",
              "after",
              "either"
            ],
            "default": "either"
          },
          "case_insensitive": {
            "type": "boolean",
            "default": false
          },
          "mode": {
            "type": "string",
            "enum": [
              "document",
              "page"
            ],
            "default": "document"
          },
          "min_hits": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          }
        }
      },
      "CustomRuleUpdateRequest": {
        "title": "CustomRuleUpdateRequest",
        "description": "Partial update. Rule ID, type, ownership, provenance, timestamps, and active status are immutable. Supply only fields applicable to the stored rule type.",
        "anyOf": [
          {
            "$ref": "#/components/schemas/CustomRuleUpdateAiSignalRequest"
          },
          {
            "$ref": "#/components/schemas/CustomRuleUpdateRegexRequest"
          },
          {
            "$ref": "#/components/schemas/CustomRuleUpdateRequireNearRequest"
          }
        ]
      },
      "CustomRuleUpdateAiSignalRequest": {
        "type": "object",
        "title": "CustomRuleUpdateAiSignalRequest",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "description": "Allowed only when the stored rule type is AI_SIGNAL"
          },
          "keywords": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1
            }
          },
          "window": {
            "type": "string",
            "enum": [
              "sentence",
              "paragraph",
              "document"
            ]
          }
        }
      },
      "CustomRuleUpdateRegexRequest": {
        "type": "object",
        "title": "CustomRuleUpdateRegexRequest",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "regex_pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Allowed only when the stored rule type is REGEX"
          }
        }
      },
      "CustomRuleUpdateRequireNearRequest": {
        "type": "object",
        "title": "CustomRuleUpdateRequireNearRequest",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "fix_note": {
            "type": "string",
            "nullable": true,
            "minLength": 1
          },
          "anchor": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512
          },
          "near": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          "distance_chars": {
            "type": "integer",
            "minimum": 0,
            "default": 100
          },
          "direction": {
            "type": "string",
            "enum": [
              "before",
              "after",
              "either"
            ],
            "default": "either"
          },
          "case_insensitive": {
            "type": "boolean",
            "default": false
          },
          "mode": {
            "type": "string",
            "enum": [
              "document",
              "page"
            ],
            "default": "document"
          },
          "min_hits": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          }
        }
      },
      "CustomRuleResponse": {
        "type": "object",
        "title": "CustomRuleResponse",
        "required": [
          "rule_id",
          "name",
          "type",
          "action",
          "severity",
          "source",
          "active",
          "created_at"
        ],
        "properties": {
          "rule_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "AI_SIGNAL",
              "REGEX",
              "REQUIRE_NEAR"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "fix",
              "flag"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "do_not_send",
              "send_with_caution"
            ]
          },
          "suggested_text": {
            "type": "string",
            "minLength": 1
          },
          "source": {
            "type": "string",
            "enum": [
              "custom"
            ]
          },
          "active": {
            "type": "boolean"
          },
          "fix_note": {
            "type": "string"
          },
          "prompt": {
            "type": "string"
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "window": {
            "type": "string",
            "enum": [
              "sentence",
              "paragraph",
              "document"
            ]
          },
          "regex_pattern": {
            "type": "string"
          },
          "anchor": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512
          },
          "near": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          "distance_chars": {
            "type": "integer",
            "minimum": 0,
            "default": 100
          },
          "direction": {
            "type": "string",
            "enum": [
              "before",
              "after",
              "either"
            ],
            "default": "either"
          },
          "case_insensitive": {
            "type": "boolean",
            "default": false
          },
          "mode": {
            "type": "string",
            "enum": [
              "document",
              "page"
            ],
            "default": "document"
          },
          "min_hits": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          },
          "import_id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ActiveRulesListResponse": {
        "type": "object",
        "title": "ActiveRulesListResponse",
        "properties": {
          "customer_id": {
            "type": "string"
          },
          "default_rules": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rule_id": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "AI_SIGNAL",
                    "REGEX"
                  ]
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "default"
                  ]
                },
                "active": {
                  "type": "boolean"
                },
                "simulatable": {
                  "type": "boolean",
                  "description": "Whether this rule can be run via the simulate endpoint (internal-only, not part of this public spec). Non-simulatable rules are still listed so the UI can render them disabled."
                },
                "rule_pack_id": {
                  "type": "string"
                },
                "rule_pack_scenario_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "threshold": {
                  "type": "number",
                  "nullable": true
                },
                "operator": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "custom_rules": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rule_id": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "AI_SIGNAL",
                    "REGEX",
                    "REQUIRE_NEAR"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "do_not_send",
                    "send_with_caution"
                  ]
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "custom"
                  ]
                },
                "active": {
                  "type": "boolean"
                },
                "simulatable": {
                  "type": "boolean"
                },
                "import_id": {
                  "type": "string"
                },
                "action": {
                  "type": "string",
                  "enum": [
                    "fix",
                    "flag"
                  ]
                },
                "window": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string"
                },
                "anchor": {
                  "type": "string",
                  "maxLength": 512
                },
                "near": {
                  "type": "array",
                  "minItems": 1,
                  "items": {
                    "type": "string",
                    "maxLength": 512
                  }
                },
                "distance_chars": {
                  "type": "integer",
                  "minimum": 0,
                  "default": 100
                },
                "direction": {
                  "type": "string",
                  "enum": [
                    "before",
                    "after",
                    "either"
                  ],
                  "default": "either"
                },
                "case_insensitive": {
                  "type": "boolean",
                  "default": false
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "document",
                    "page"
                  ],
                  "default": "document"
                },
                "min_hits": {
                  "type": "integer",
                  "minimum": 1,
                  "default": 1
                }
              }
            }
          },
          "default_rules_count": {
            "type": "integer"
          },
          "default_rules_active_count": {
            "type": "integer"
          },
          "custom_rules_count": {
            "type": "integer"
          },
          "custom_rules_active_count": {
            "type": "integer"
          },
          "total_rules_count": {
            "type": "integer"
          },
          "total_active_count": {
            "type": "integer"
          }
        }
      },
      "ActivityRuleSummary": {
        "type": "object",
        "title": "ActivityRuleSummary",
        "required": [
          "id",
          "name",
          "severity",
          "type",
          "policy"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "AI_SIGNAL",
              "REGEX",
              "REQUIRE_NEAR"
            ]
          },
          "policy": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "ActivityRule": {
        "type": "object",
        "title": "ActivityRule",
        "required": [
          "id",
          "name",
          "severity",
          "type",
          "policy"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "AI_SIGNAL",
              "REGEX",
              "REQUIRE_NEAR"
            ]
          },
          "policy": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "snippet": {
            "type": "string"
          },
          "suggested_text": {
            "type": "string"
          },
          "fix_note": {
            "type": "string"
          }
        }
      },
      "ActivitySummary": {
        "type": "object",
        "title": "ActivitySummary",
        "required": [
          "activity_id",
          "occurred_at",
          "event_name",
          "status",
          "source",
          "agent",
          "rules"
        ],
        "properties": {
          "activity_id": {
            "type": "string"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "event_name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "auto_fixed",
              "blocked",
              "warned",
              "passed"
            ]
          },
          "source": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "agent": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActivityRuleSummary"
            }
          },
          "rule_count": {
            "type": "integer"
          },
          "truncated_rule_count": {
            "type": "integer"
          }
        }
      },
      "Activity": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ActivitySummary"
          },
          {
            "type": "object",
            "properties": {
              "endpoint": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "rules": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ActivityRule"
                }
              },
              "timeline": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "step": {
                      "type": "string"
                    },
                    "at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ActivityListResponse": {
        "type": "object",
        "title": "ActivityListResponse",
        "required": [
          "items",
          "meta"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActivitySummary"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "meta": {
            "type": "object",
            "required": [
              "total_pages",
              "current_page"
            ],
            "properties": {
              "total_pages": {
                "type": "integer",
                "minimum": 0
              },
              "current_page": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      },
      "RulepackCatalogResponse": {
        "type": "object",
        "title": "RulepackCatalogResponse",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RulepackSummary"
                }
              },
              {
                "$ref": "#/components/schemas/RulepackDetail"
              }
            ]
          },
          "meta": {
            "type": "object",
            "nullable": true,
            "properties": {
              "current_page": {
                "type": "integer"
              },
              "per_page": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              },
              "last_page": {
                "type": "integer"
              }
            }
          }
        }
      },
      "RulepackSummary": {
        "type": "object",
        "title": "RulepackSummary",
        "required": [
          "id",
          "name",
          "type",
          "engine",
          "active",
          "total_rules",
          "active_rules",
          "inactive_rules"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "managed",
              "custom"
            ]
          },
          "engine": {
            "type": "string",
            "enum": [
              "anchor_3_0"
            ]
          },
          "active": {
            "type": "boolean"
          },
          "total_rules": {
            "type": "integer"
          },
          "active_rules": {
            "type": "integer"
          },
          "inactive_rules": {
            "type": "integer"
          },
          "publisher": {
            "type": "string",
            "nullable": true
          },
          "version": {
            "type": "string",
            "nullable": true
          },
          "domain": {
            "type": "string",
            "nullable": true
          },
          "jurisdictions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "last_updated": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "RulepackDetail": {
        "type": "object",
        "title": "RulepackDetail",
        "required": [
          "id",
          "name",
          "type",
          "engine",
          "active",
          "total_rules",
          "active_rules",
          "inactive_rules",
          "rules"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "managed",
              "custom"
            ]
          },
          "engine": {
            "type": "string",
            "enum": [
              "anchor_3_0"
            ]
          },
          "active": {
            "type": "boolean"
          },
          "total_rules": {
            "type": "integer"
          },
          "active_rules": {
            "type": "integer"
          },
          "inactive_rules": {
            "type": "integer"
          },
          "publisher": {
            "type": "string",
            "nullable": true
          },
          "version": {
            "type": "string",
            "nullable": true
          },
          "domain": {
            "type": "string",
            "nullable": true
          },
          "jurisdictions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "last_updated": {
            "type": "string",
            "nullable": true
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "type",
                "severity",
                "verdict",
                "active",
                "citations",
                "remediation"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "nullable": true
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "AI_SIGNAL",
                    "REGEX",
                    "COMPOSITE",
                    "LEXICON",
                    "REQUIRE_NEAR",
                    "CUSTOM"
                  ]
                },
                "severity": {
                  "type": "string",
                  "nullable": true
                },
                "verdict": {
                  "type": "string",
                  "enum": [
                    "do_not_send",
                    "send_with_caution"
                  ]
                },
                "active": {
                  "type": "boolean"
                },
                "lifecycle": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "status": {
                      "type": "string",
                      "nullable": true
                    },
                    "effective_from": {
                      "type": "string",
                      "nullable": true
                    },
                    "last_reviewed": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "citations": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "framework": {
                        "type": "string",
                        "nullable": true
                      },
                      "cite": {
                        "type": "string",
                        "nullable": true
                      },
                      "jurisdictions": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "remediation": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "nullable": true
                    },
                    "note": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ValidationVerdict": {
        "type": "object",
        "title": "ValidationVerdict",
        "description": "The `result` object of a completed v3 envelope. Optional fields appear only when the serving engine produced them.",
        "properties": {
          "overall_status": {
            "type": "string",
            "enum": [
              "approved",
              "needs_changes",
              "send with caution",
              "do not send",
              "unknown"
            ],
            "description": "`send with caution` can come from a trained adapter; `unknown` appears only when the record carries no verdict."
          },
          "summary": {
            "type": "object",
            "properties": {
              "do_not_send": {
                "type": "integer"
              },
              "send_with_caution": {
                "type": "integer"
              },
              "document_pages": {
                "type": "integer"
              }
            }
          },
          "violating_line_count": {
            "type": "integer"
          },
          "compliant_line_count": {
            "type": "integer"
          },
          "total_line_count": {
            "type": "integer"
          },
          "revalidation_recommended": {
            "type": "boolean",
            "description": "True when >1 fix found, suggesting re-scan after Fix All"
          },
          "relevant_rule_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Rule IDs the serving Anchor model identified as relevant to the content."
          },
          "flagged_rules": {
            "type": "array",
            "description": "Mini's per-rule, whole-message verdicts. Present instead of violations because Mini has no line-level head.",
            "items": {
              "$ref": "#/components/schemas/V3FlaggedRule"
            }
          },
          "any_violation_detected": {
            "type": "boolean",
            "description": "Mini's independent whole-message violation head. It can be true even when flagged_rules is empty."
          },
          "any_violation_p": {
            "type": "number",
            "description": "Probability behind any_violation_detected (Mini only)."
          },
          "units": {
            "type": "integer",
            "description": "Billed validations for this call: zero when no input tokens were consumed, otherwise the ceiling of input tokens divided by 2,000. Public responses report units and cost instead of raw token counts."
          },
          "cost": {
            "$ref": "#/components/schemas/ValidationCost"
          },
          "violations_by_line": {
            "type": "array",
            "description": "One entry per violating line. Present but empty for fully compliant documents.",
            "items": {
              "$ref": "#/components/schemas/LineViolation"
            }
          },
          "fixes": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "fixes_group": {
            "type": "object"
          },
          "all_violations": {
            "type": "array",
            "description": "Present on email-style validation \u2014 merged signal + regex violations",
            "items": {
              "$ref": "#/components/schemas/EmailViolation"
            }
          },
          "violations": {
            "type": "array",
            "description": "Present on email-style validation \u2014 the AI-signal subset of all_violations",
            "items": {
              "$ref": "#/components/schemas/EmailViolation"
            }
          },
          "metadata": {
            "type": "object",
            "description": "Document metadata submitted with the request"
          },
          "training_run_id": {
            "type": "string",
            "description": "Training Studio run ID when a trained adapter produced this result."
          }
        }
      }
    }
  }
}
