{
  "components": {
    "schemas": {
      "ActionCount": {
        "description": "How many jobs had one action, by actor, kind, mode and whether it was applied.",
        "properties": {
          "action": {
            "title": "Action",
            "type": "string"
          },
          "actor": {
            "title": "Actor",
            "type": "string"
          },
          "applied": {
            "title": "Applied",
            "type": "boolean"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "kind": {
            "enum": [
              "inline",
              "responder"
            ],
            "title": "Kind",
            "type": "string"
          },
          "mode": {
            "enum": [
              "observe",
              "enforce"
            ],
            "title": "Mode",
            "type": "string"
          }
        },
        "required": [
          "actor",
          "kind",
          "action",
          "mode",
          "applied",
          "count"
        ],
        "title": "ActionCount",
        "type": "object"
      },
      "ActionRecord": {
        "description": "What one actor did with a job's email, or would have done in observe.",
        "properties": {
          "action": {
            "description": "What it did: the retriever's word, or respond",
            "title": "Action",
            "type": "string"
          },
          "actor": {
            "description": "The plugin that acted: the retriever, or the responder",
            "title": "Actor",
            "type": "string"
          },
          "applied": {
            "description": "Whether it was done; never in observe",
            "title": "Applied",
            "type": "boolean"
          },
          "at": {
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "job_id": {
            "title": "Job Id",
            "type": "string"
          },
          "kind": {
            "enum": [
              "inline",
              "responder"
            ],
            "title": "Kind",
            "type": "string"
          },
          "mode": {
            "description": "The job's mode",
            "enum": [
              "observe",
              "enforce"
            ],
            "title": "Mode",
            "type": "string"
          }
        },
        "required": [
          "job_id",
          "email_id",
          "actor",
          "kind",
          "action",
          "mode",
          "applied",
          "at"
        ],
        "title": "ActionRecord",
        "type": "object"
      },
      "ActionReport": {
        "description": "The body of ``POST /internal/jobs/{job_id}/action``: what a retriever\ndid with a message (or, in observe, would have done).",
        "properties": {
          "action": {
            "pattern": "^[a-z][a-z0-9_]{0,31}$",
            "title": "Action",
            "type": "string"
          },
          "applied": {
            "title": "Applied",
            "type": "boolean"
          }
        },
        "required": [
          "action",
          "applied"
        ],
        "title": "ActionReport",
        "type": "object"
      },
      "ActionSummaryResponse": {
        "description": "What was done with the emails of the last days, or would have been in observe.",
        "properties": {
          "actions": {
            "description": "The actions counted by actor, kind, action, mode and whether applied",
            "items": {
              "$ref": "#/components/schemas/ActionCount"
            },
            "title": "Actions",
            "type": "array"
          },
          "connections": {
            "additionalProperties": {
              "enum": [
                "observe",
                "enforce"
              ],
              "type": "string"
            },
            "description": "Every mail connection (mail retriever) of spot.yaml by plugin id, with its mode (observe when its entry has none), whatever its actions",
            "title": "Connections",
            "type": "object"
          },
          "days": {
            "description": "The window: the last days × 24 hours, up to now",
            "title": "Days",
            "type": "integer"
          }
        },
        "required": [
          "days",
          "actions",
          "connections"
        ],
        "title": "ActionSummaryResponse",
        "type": "object"
      },
      "Address": {
        "description": "A mailbox from an address header.",
        "properties": {
          "address": {
            "description": "As written; may be invalid, never rejected",
            "title": "Address",
            "type": "string"
          },
          "display_name": {
            "default": "",
            "title": "Display Name",
            "type": "string"
          },
          "domain": {
            "default": "",
            "description": "As browsers read a host (WHATWG URL Standard): UTS 46-mapped (lower-cased...), A-labels decoded, no trailing dots; one that can be no DNS name only lower-cased: over 1 012 characters, a label over 252 or over 63 combining marks in a row as UTS 46 maps them, invisible ones aside (DNS allows 253 and 63, NFC composes at most 4 code points into one, and an A-label spells each mark with a character at least); one whose A-labels not yet decoded do not fit in the 32 768 characters of distinct A-labels a message decodes keeps all its A-labels encoded, as UTS 46 maps them (host_limit)",
            "title": "Domain",
            "type": "string"
          }
        },
        "required": [
          "address"
        ],
        "title": "Address",
        "type": "object"
      },
      "AggregationMethod": {
        "description": "How the analyzers' answers become the email's verdict.",
        "enum": [
          "most_confident",
          "weighted_mean",
          "mean",
          "max",
          "majority"
        ],
        "title": "AggregationMethod",
        "type": "string"
      },
      "AnalysisListResponse": {
        "description": "Paginated analysis jobs list response.",
        "properties": {
          "jobs": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Jobs",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "jobs",
          "total",
          "limit",
          "offset"
        ],
        "title": "AnalysisListResponse",
        "type": "object"
      },
      "AnalysisRequest": {
        "description": "An analysis asked for: the workflow to run.",
        "properties": {
          "workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "None: the workflow of the email's route, else the platform's default",
            "title": "Workflow Id"
          }
        },
        "title": "AnalysisRequest",
        "type": "object"
      },
      "AnalysisResponse": {
        "description": "An accepted submission: the job analysing the stored email.",
        "properties": {
          "email_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email Id"
          },
          "job_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Job Id"
          },
          "message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Message"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status"
          },
          "workflow": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workflow"
          }
        },
        "title": "AnalysisResponse",
        "type": "object"
      },
      "AnalysisStatsResponse": {
        "description": "Aggregated analysis statistics.",
        "properties": {
          "by_status": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Analyses by status, every status",
            "title": "By Status",
            "type": "object"
          },
          "by_verdict": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Analyses completed or failed by verdict, every class present; a failed one counts as unknown",
            "propertyNames": {
              "$ref": "#/components/schemas/VerdictClass"
            },
            "title": "By Verdict",
            "type": "object"
          },
          "total": {
            "description": "Analyses completed or failed; a cancelled job counts only in by_status",
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "total",
          "by_status",
          "by_verdict"
        ],
        "title": "AnalysisStatsResponse",
        "type": "object"
      },
      "AnalysisSubmission": {
        "description": "A message submitted for analysis, as JSON.",
        "properties": {
          "raw": {
            "description": "The RFC 5322 message, base64-encoded",
            "format": "base64",
            "title": "Raw",
            "type": "string"
          },
          "workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "None: the workflow of the email's route, else the platform's default",
            "title": "Workflow Id"
          }
        },
        "required": [
          "raw"
        ],
        "title": "AnalysisSubmission",
        "type": "object"
      },
      "AnalyzerAnswer": {
        "additionalProperties": false,
        "description": "One analyzer's answer on one email of a run: its verdict and its time, never its text.",
        "properties": {
          "analyzer_id": {
            "maxLength": 100,
            "title": "Analyzer Id",
            "type": "string"
          },
          "execution_time_ms": {
            "minimum": 0.0,
            "title": "Execution Time Ms",
            "type": "integer"
          },
          "score": {
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Score",
            "type": "integer"
          },
          "status": {
            "enum": [
              "success",
              "unknown",
              "error",
              "timeout",
              "skipped"
            ],
            "title": "Status",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          }
        },
        "required": [
          "analyzer_id",
          "status",
          "verdict",
          "score",
          "execution_time_ms"
        ],
        "title": "AnalyzerAnswer",
        "type": "object"
      },
      "AnalyzerConfig": {
        "description": "Configuration for a single analyzer in a workflow stage.",
        "properties": {
          "condition": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Conditional execution expression",
            "title": "Condition"
          },
          "failure_strategy": {
            "$ref": "#/components/schemas/FailureStrategy",
            "default": "retry",
            "description": "Failure handling strategy"
          },
          "id": {
            "description": "Analyzer ID",
            "title": "Id",
            "type": "string"
          },
          "required": {
            "default": false,
            "description": "Whether this analyzer is required",
            "title": "Required",
            "type": "boolean"
          },
          "retry_config": {
            "$ref": "#/components/schemas/RetryConfig",
            "description": "Retry configuration"
          },
          "timeout_ms": {
            "default": 30000,
            "description": "Timeout in milliseconds",
            "maximum": 300000.0,
            "minimum": 1000.0,
            "title": "Timeout Ms",
            "type": "integer"
          },
          "weight": {
            "default": 1.0,
            "description": "Weight for result aggregation",
            "maximum": 10.0,
            "minimum": 0.0,
            "title": "Weight",
            "type": "number"
          }
        },
        "required": [
          "id"
        ],
        "title": "AnalyzerConfig",
        "type": "object"
      },
      "AnalyzerMetrics": {
        "additionalProperties": false,
        "description": "One analyzer's metrics on its own answers, and its latency.",
        "properties": {
          "accuracy": {
            "default": 0.0,
            "title": "Accuracy",
            "type": "number"
          },
          "avg_score": {
            "default": 0.0,
            "title": "Avg Score",
            "type": "number"
          },
          "completed": {
            "default": 0,
            "title": "Completed",
            "type": "integer"
          },
          "f1": {
            "default": 0.0,
            "title": "F1",
            "type": "number"
          },
          "f2": {
            "default": 0.0,
            "title": "F2",
            "type": "number"
          },
          "failed": {
            "default": 0,
            "title": "Failed",
            "type": "integer"
          },
          "fn": {
            "default": 0,
            "title": "Fn",
            "type": "integer"
          },
          "fnr": {
            "default": 0.0,
            "title": "Fnr",
            "type": "number"
          },
          "fp": {
            "default": 0,
            "title": "Fp",
            "type": "integer"
          },
          "fpr": {
            "default": 0.0,
            "title": "Fpr",
            "type": "number"
          },
          "fpr_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fpr Ci"
          },
          "latency": {
            "$ref": "#/components/schemas/Latency"
          },
          "precision": {
            "default": 0.0,
            "title": "Precision",
            "type": "number"
          },
          "precision_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Precision Ci"
          },
          "recall": {
            "default": 0.0,
            "title": "Recall",
            "type": "number"
          },
          "recall_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Recall Ci"
          },
          "tn": {
            "default": 0,
            "title": "Tn",
            "type": "integer"
          },
          "total": {
            "default": 0,
            "title": "Total",
            "type": "integer"
          },
          "tp": {
            "default": 0,
            "title": "Tp",
            "type": "integer"
          },
          "unknown": {
            "default": 0,
            "title": "Unknown",
            "type": "integer"
          }
        },
        "required": [
          "latency"
        ],
        "title": "AnalyzerMetrics",
        "type": "object"
      },
      "AnalyzerOutcome": {
        "description": "What one analyzer answered, or why it did not.",
        "properties": {
          "analyzer_id": {
            "title": "Analyzer Id",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "execution_time_ms": {
            "title": "Execution Time Ms",
            "type": "integer"
          },
          "stage": {
            "title": "Stage",
            "type": "string"
          },
          "status": {
            "enum": [
              "success",
              "unknown",
              "error",
              "timeout",
              "skipped"
            ],
            "title": "Status",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/AnalyzerVerdict"
          }
        },
        "required": [
          "analyzer_id",
          "stage",
          "status",
          "verdict",
          "execution_time_ms"
        ],
        "title": "AnalyzerOutcome",
        "type": "object"
      },
      "AnalyzerSnapshot": {
        "description": "An analyzer of the workflow when the run started.",
        "properties": {
          "image": {
            "description": "Its image in spot.yaml; empty for a manual entry",
            "title": "Image",
            "type": "string"
          },
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The version it reported, else spot.yaml's; None for neither",
            "title": "Version"
          }
        },
        "required": [
          "version",
          "image"
        ],
        "title": "AnalyzerSnapshot",
        "type": "object"
      },
      "AnalyzerVerdict": {
        "description": "An analyzer's answer to ``POST /internal/analyze``.\n\nAn analyzer that can't decide answers ``verdict=\"unknown\"`` with a\n``summary`` saying why.",
        "properties": {
          "confidence": {
            "description": "How sure the analyzer is of its own verdict",
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Confidence",
            "type": "integer"
          },
          "details": {
            "additionalProperties": true,
            "description": "Analyzer-specific, stored, shown raw",
            "title": "Details",
            "type": "object"
          },
          "model_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model Version"
          },
          "score": {
            "description": "How likely the email is malicious",
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Score",
            "type": "integer"
          },
          "signals": {
            "items": {
              "$ref": "#/components/schemas/Signal"
            },
            "title": "Signals",
            "type": "array"
          },
          "summary": {
            "default": "",
            "title": "Summary",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          }
        },
        "required": [
          "verdict",
          "score",
          "confidence"
        ],
        "title": "AnalyzerVerdict",
        "type": "object"
      },
      "Anomaly": {
        "description": "Something the parser read past: an ambiguity mail clients may read\ndifferently, or a bound it hit. The codes are in ``spot_sdk.mime``.",
        "properties": {
          "code": {
            "description": "One of the spot_sdk.mime anomaly codes",
            "title": "Code",
            "type": "string"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The values concerned, as JSON strings: at most 200 bytes of JSON",
            "title": "Detail"
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "\"part:1.2\", \"header:Subject@1\", \"param:Content-Type.boundary@1.2\", \"url:3\" (the view's URL index), ..., \"envelope\", \"source\": at most 200 bytes of JSON",
            "title": "Location"
          }
        },
        "required": [
          "code"
        ],
        "title": "Anomaly",
        "type": "object"
      },
      "AttributedSignal": {
        "description": "A signal and the analyzer that emitted it.",
        "properties": {
          "analyzer_id": {
            "title": "Analyzer Id",
            "type": "string"
          },
          "evidence": {
            "default": "",
            "description": "Short human-readable proof",
            "title": "Evidence",
            "type": "string"
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "\"header:From\", \"url:3\", \"part:1.2\"",
            "title": "Location"
          },
          "name": {
            "description": "e.g. auth.dmarc_fail",
            "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)+$",
            "title": "Name",
            "type": "string"
          },
          "weight": {
            "description": "Contribution, in score points",
            "maximum": 100.0,
            "minimum": -100.0,
            "title": "Weight",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "weight",
          "analyzer_id"
        ],
        "title": "AttributedSignal",
        "type": "object"
      },
      "AuditEntry": {
        "description": "One entry of the audit log.",
        "properties": {
          "action": {
            "description": "What was done, as `<area>.<verb>`: `plugin.install`",
            "title": "Action",
            "type": "string"
          },
          "actor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The user or client's name; None when unknown",
            "title": "Actor"
          },
          "actor_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The user or client's id, which a rename keeps",
            "title": "Actor Id"
          },
          "at": {
            "description": "When it happened, in UTC",
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "correlation_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The request's correlation ID",
            "title": "Correlation Id"
          },
          "details": {
            "additionalProperties": true,
            "description": "What changed: names, a role, a version; never a secret value, a token or content. A failure adds `status`, the HTTP status",
            "title": "Details",
            "type": "object"
          },
          "ip_address": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The address the gateway saw",
            "title": "Ip Address"
          },
          "outcome": {
            "enum": [
              "success",
              "failure"
            ],
            "title": "Outcome",
            "type": "string"
          },
          "target": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What it was done to: a plugin, a username, a source, an email, a document or a queue",
            "title": "Target"
          },
          "user_agent": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The client's `User-Agent`",
            "title": "User Agent"
          }
        },
        "required": [
          "at",
          "actor",
          "action",
          "outcome"
        ],
        "title": "AuditEntry",
        "type": "object"
      },
      "AuditPage": {
        "description": "A page of the audit log, newest first.",
        "properties": {
          "entries": {
            "items": {
              "$ref": "#/components/schemas/AuditEntry"
            },
            "title": "Entries",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "description": "The entries that pass the filters, on every page",
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "entries",
          "total",
          "limit",
          "offset"
        ],
        "title": "AuditPage",
        "type": "object"
      },
      "AuthResult": {
        "description": "One result of an ``Authentication-Results`` header.",
        "properties": {
          "authserv_id": {
            "title": "Authserv Id",
            "type": "string"
          },
          "method": {
            "description": "spf, dkim, dmarc, arc, ...",
            "title": "Method",
            "type": "string"
          },
          "properties": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "e.g. {\"header.d\": \"example.com\", \"smtp.mailfrom\": \"...\"}",
            "title": "Properties",
            "type": "object"
          },
          "result": {
            "description": "pass, fail, softfail, neutral, none, temperror, permerror, ...",
            "title": "Result",
            "type": "string"
          }
        },
        "required": [
          "authserv_id",
          "method",
          "result"
        ],
        "title": "AuthResult",
        "type": "object"
      },
      "BenchmarkEmailResult-Input": {
        "additionalProperties": false,
        "description": "One email's outcome in a run: its truth, the workflow's verdict, each analyzer's answer.",
        "properties": {
          "analyzers": {
            "items": {
              "$ref": "#/components/schemas/AnalyzerAnswer"
            },
            "maxItems": 100,
            "title": "Analyzers",
            "type": "array"
          },
          "baseline_job_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The production analysis that verdict is of",
            "title": "Baseline Job Id"
          },
          "baseline_verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ],
            "description": "Production's verdict on a stored email"
          },
          "dataset": {
            "description": "Its dataset; mail for the stored mail",
            "maxLength": 100,
            "minLength": 1,
            "title": "Dataset",
            "type": "string"
          },
          "decided_by": {
            "anyOf": [
              {
                "maxLength": 200,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Decided By"
          },
          "duration_ms": {
            "anyOf": [
              {
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration Ms"
          },
          "error": {
            "anyOf": [
              {
                "maxLength": 500,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it failed, never the content",
            "title": "Error"
          },
          "ground_truth": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "True for phishing; None for a stored email no analyst labelled",
            "title": "Ground Truth"
          },
          "job_id": {
            "anyOf": [
              {
                "maxLength": 64,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The job of a run spot-cli reports",
            "title": "Job Id"
          },
          "key": {
            "description": "The email in its dataset: miltchev_12, or a stored email's id",
            "maxLength": 255,
            "minLength": 1,
            "title": "Key",
            "type": "string"
          },
          "score": {
            "anyOf": [
              {
                "maximum": 100.0,
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score"
          },
          "status": {
            "enum": [
              "completed",
              "failed",
              "timeout",
              "error"
            ],
            "title": "Status",
            "type": "string"
          },
          "verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ]
          },
          "workflow_version": {
            "anyOf": [
              {
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workflow Version"
          }
        },
        "required": [
          "key",
          "dataset",
          "ground_truth",
          "status"
        ],
        "title": "BenchmarkEmailResult",
        "type": "object"
      },
      "BenchmarkEmailResult-Output": {
        "additionalProperties": false,
        "description": "One email's outcome in a run: its truth, the workflow's verdict, each analyzer's answer.",
        "properties": {
          "analyzers": {
            "items": {
              "$ref": "#/components/schemas/AnalyzerAnswer"
            },
            "maxItems": 100,
            "title": "Analyzers",
            "type": "array"
          },
          "baseline_job_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The production analysis that verdict is of",
            "title": "Baseline Job Id"
          },
          "baseline_verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ],
            "description": "Production's verdict on a stored email"
          },
          "dataset": {
            "description": "Its dataset; mail for the stored mail",
            "maxLength": 100,
            "minLength": 1,
            "title": "Dataset",
            "type": "string"
          },
          "decided_by": {
            "anyOf": [
              {
                "maxLength": 200,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Decided By"
          },
          "duration_ms": {
            "anyOf": [
              {
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration Ms"
          },
          "error": {
            "anyOf": [
              {
                "maxLength": 500,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why it failed, never the content",
            "title": "Error"
          },
          "ground_truth": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "True for phishing; None for a stored email no analyst labelled",
            "title": "Ground Truth"
          },
          "job_id": {
            "anyOf": [
              {
                "maxLength": 64,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The job of a run spot-cli reports",
            "title": "Job Id"
          },
          "key": {
            "description": "The email in its dataset: miltchev_12, or a stored email's id",
            "maxLength": 255,
            "minLength": 1,
            "title": "Key",
            "type": "string"
          },
          "score": {
            "anyOf": [
              {
                "maximum": 100.0,
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score"
          },
          "status": {
            "enum": [
              "completed",
              "failed",
              "timeout",
              "error"
            ],
            "title": "Status",
            "type": "string"
          },
          "verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ]
          },
          "workflow_version": {
            "anyOf": [
              {
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workflow Version"
          }
        },
        "required": [
          "key",
          "dataset",
          "ground_truth",
          "status"
        ],
        "title": "BenchmarkEmailResult",
        "type": "object"
      },
      "BenchmarkResultPage": {
        "description": "A page of a run's results: verdicts, scores, names and times, never an email's content.",
        "properties": {
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/BenchmarkEmailResult-Output"
            },
            "title": "Results",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "results",
          "total",
          "limit",
          "offset"
        ],
        "title": "BenchmarkResultPage",
        "type": "object"
      },
      "BenchmarkRun": {
        "description": "A benchmark run as it is stored, with the emails recorded so far.",
        "properties": {
          "analyzers": {
            "additionalProperties": {
              "$ref": "#/components/schemas/AnalyzerSnapshot"
            },
            "title": "Analyzers",
            "type": "object"
          },
          "config_commit": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The last commit of spot.yaml's history; None without one",
            "title": "Config Commit"
          },
          "core_version": {
            "title": "Core Version",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "created_by": {
            "title": "Created By",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Finished At"
          },
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "metrics": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RunMetrics"
              },
              {
                "type": "null"
              }
            ]
          },
          "recorded": {
            "title": "Recorded",
            "type": "integer"
          },
          "runner": {
            "enum": [
              "client",
              "platform"
            ],
            "title": "Runner",
            "type": "string"
          },
          "selection": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FileSelection"
              },
              {
                "$ref": "#/components/schemas/DatasetSelection"
              },
              {
                "$ref": "#/components/schemas/MailSelection"
              }
            ],
            "title": "Selection"
          },
          "selection_sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Selection Sha256"
          },
          "status": {
            "enum": [
              "running",
              "completed",
              "cancelled",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          },
          "workflow_id": {
            "title": "Workflow Id",
            "type": "string"
          },
          "workflow_version": {
            "title": "Workflow Version",
            "type": "integer"
          }
        },
        "required": [
          "workflow_version",
          "config_commit",
          "core_version",
          "analyzers",
          "id",
          "runner",
          "workflow_id",
          "selection",
          "selection_sha256",
          "total",
          "recorded",
          "status",
          "error",
          "created_by",
          "created_at",
          "finished_at",
          "metrics"
        ],
        "title": "BenchmarkRun",
        "type": "object"
      },
      "BenchmarkRunPage": {
        "description": "A page of runs, newest first.",
        "properties": {
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "runs": {
            "items": {
              "$ref": "#/components/schemas/BenchmarkRun"
            },
            "title": "Runs",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "runs",
          "total",
          "limit",
          "offset"
        ],
        "title": "BenchmarkRunPage",
        "type": "object"
      },
      "BenchmarkStart": {
        "additionalProperties": false,
        "description": "A run to start: the workflow, and the emails it takes.",
        "properties": {
          "emails": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FileSelection"
              },
              {
                "$ref": "#/components/schemas/DatasetSelection"
              },
              {
                "$ref": "#/components/schemas/MailSelection"
              }
            ],
            "title": "Emails"
          },
          "workflow_id": {
            "maxLength": 100,
            "title": "Workflow Id",
            "type": "string"
          }
        },
        "required": [
          "workflow_id",
          "emails"
        ],
        "title": "BenchmarkStart",
        "type": "object"
      },
      "Body_client_credentials_auth_client_credentials_post": {
        "properties": {
          "client_id": {
            "title": "Client Id",
            "type": "string"
          },
          "client_secret": {
            "title": "Client Secret",
            "type": "string"
          }
        },
        "required": [
          "client_id",
          "client_secret"
        ],
        "title": "Body_client_credentials_auth_client_credentials_post",
        "type": "object"
      },
      "Body_login_auth_token_post": {
        "properties": {
          "client_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Id"
          },
          "client_secret": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "format": "password",
            "title": "Client Secret"
          },
          "grant_type": {
            "anyOf": [
              {
                "pattern": "^password$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Grant Type"
          },
          "password": {
            "format": "password",
            "title": "Password",
            "type": "string"
          },
          "scope": {
            "default": "",
            "title": "Scope",
            "type": "string"
          },
          "username": {
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "username",
          "password"
        ],
        "title": "Body_login_auth_token_post",
        "type": "object"
      },
      "CancelResponse": {
        "description": "A job cancelled, now or before.",
        "properties": {
          "job_id": {
            "title": "Job Id",
            "type": "string"
          },
          "status": {
            "const": "cancelled",
            "default": "cancelled",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "job_id"
        ],
        "title": "CancelResponse",
        "type": "object"
      },
      "CatalogResponse": {
        "description": "Response model for the plugin catalog.",
        "properties": {
          "last_refresh": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Refresh"
          },
          "plugins": {
            "items": {
              "$ref": "#/components/schemas/PluginInfoResponse"
            },
            "title": "Plugins",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "plugins",
          "total",
          "last_refresh"
        ],
        "title": "CatalogResponse",
        "type": "object"
      },
      "CommitDetail": {
        "description": "A commit, what it changed, and what a rollback to it would change now.",
        "properties": {
          "commit": {
            "$ref": "#/components/schemas/CommitSummary"
          },
          "diff": {
            "description": "A unified diff from the commit before it (the first: from no file); each sealed secret reads (sealed), or (sealed, changed)",
            "title": "Diff",
            "type": "string"
          },
          "rollback_diff": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "What a rollback to it would change, from the settings in force as SPOT writes them (a hand edit of spot.yaml not yet reloaded is not shown), plugins: aside, sealed secrets hidden alike; null when its spot.yaml no longer validates",
            "title": "Rollback Diff"
          }
        },
        "required": [
          "commit",
          "diff",
          "rollback_diff"
        ],
        "title": "CommitDetail",
        "type": "object"
      },
      "CommitSummary": {
        "description": "A commit of spot.yaml's history: who changed what, and when.",
        "properties": {
          "at": {
            "description": "When the change was saved (UTC)",
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "author": {
            "description": "The user who made the change, by username; SPOT for its own",
            "title": "Author",
            "type": "string"
          },
          "id": {
            "description": "The commit's full id",
            "title": "Id",
            "type": "string"
          },
          "message": {
            "description": "What the change did",
            "title": "Message",
            "type": "string"
          },
          "sections": {
            "description": "The top-level sections of spot.yaml it changed",
            "items": {
              "type": "string"
            },
            "title": "Sections",
            "type": "array"
          },
          "short_id": {
            "description": "Its first 12 hex digits",
            "title": "Short Id",
            "type": "string"
          },
          "workflows": {
            "description": "The ids of the workflows it added, removed or changed",
            "items": {
              "type": "string"
            },
            "title": "Workflows",
            "type": "array"
          }
        },
        "required": [
          "id",
          "short_id",
          "author",
          "at",
          "message",
          "sections",
          "workflows"
        ],
        "title": "CommitSummary",
        "type": "object"
      },
      "ConfigChanges": {
        "properties": {
          "analyzers": {
            "items": {
              "type": "string"
            },
            "title": "Analyzers",
            "type": "array"
          },
          "platform": {
            "default": false,
            "title": "Platform",
            "type": "boolean"
          },
          "workflows": {
            "default": false,
            "title": "Workflows",
            "type": "boolean"
          }
        },
        "title": "ConfigChanges",
        "type": "object"
      },
      "ConfigHistoryPage": {
        "description": "A page of spot.yaml's history, newest first.",
        "properties": {
          "commits": {
            "items": {
              "$ref": "#/components/schemas/CommitSummary"
            },
            "title": "Commits",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "description": "The commits of the whole history",
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "commits",
          "total",
          "limit",
          "offset"
        ],
        "title": "ConfigHistoryPage",
        "type": "object"
      },
      "ConfigOption": {
        "description": "Single configuration option with full metadata.",
        "properties": {
          "default": {
            "description": "Default value from schema",
            "title": "Default"
          },
          "description": {
            "default": "",
            "description": "Human-readable description",
            "title": "Description",
            "type": "string"
          },
          "enum": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Allowed values if restricted",
            "title": "Enum"
          },
          "is_default": {
            "description": "True if current value equals default",
            "title": "Is Default",
            "type": "boolean"
          },
          "maximum": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Maximum value for numbers",
            "title": "Maximum"
          },
          "minimum": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Minimum value for numbers",
            "title": "Minimum"
          },
          "path": {
            "description": "Dot-notation path (e.g., 'platform.log_level')",
            "title": "Path",
            "type": "string"
          },
          "type": {
            "description": "JSON Schema type (string, boolean, integer, etc.)",
            "title": "Type",
            "type": "string"
          },
          "value": {
            "description": "Current value",
            "title": "Value"
          }
        },
        "required": [
          "path",
          "value",
          "default",
          "type",
          "is_default"
        ],
        "title": "ConfigOption",
        "type": "object"
      },
      "ConfigReloadResponse": {
        "properties": {
          "changed": {
            "$ref": "#/components/schemas/ConfigChanges"
          },
          "new_version": {
            "title": "New Version",
            "type": "string"
          },
          "old_version": {
            "title": "Old Version",
            "type": "string"
          }
        },
        "required": [
          "old_version",
          "new_version",
          "changed"
        ],
        "title": "ConfigReloadResponse",
        "type": "object"
      },
      "ConfigStatus": {
        "description": "Configuration synchronization status.",
        "properties": {
          "applied_hash": {
            "description": "Hash of last-applied configuration",
            "title": "Applied Hash",
            "type": "string"
          },
          "needs_reload": {
            "description": "True if pending differs from applied",
            "title": "Needs Reload",
            "type": "boolean"
          },
          "pending_changes": {
            "default": 0,
            "description": "Number of options changed since last reload",
            "title": "Pending Changes",
            "type": "integer"
          },
          "pending_hash": {
            "description": "Hash of current in-memory configuration",
            "title": "Pending Hash",
            "type": "string"
          }
        },
        "required": [
          "applied_hash",
          "pending_hash",
          "needs_reload"
        ],
        "title": "ConfigStatus",
        "type": "object"
      },
      "ConfigUpdateRequest": {
        "description": "Request to update a single configuration option.",
        "properties": {
          "value": {
            "description": "New value for the option",
            "title": "Value"
          }
        },
        "required": [
          "value"
        ],
        "title": "ConfigUpdateRequest",
        "type": "object"
      },
      "Consent": {
        "description": "An admin's consent to what a plugin sends outside SPOT, recorded by the installer.",
        "properties": {
          "at": {
            "description": "When, in UTC",
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "egress": {
            "$ref": "#/components/schemas/Egress",
            "description": "The egress class consented to"
          },
          "user": {
            "description": "The admin who consented",
            "title": "User",
            "type": "string"
          }
        },
        "required": [
          "egress",
          "user",
          "at"
        ],
        "title": "Consent",
        "type": "object"
      },
      "DailyAnalysisStats": {
        "description": "Analysis counts and execution times for one UTC day.",
        "properties": {
          "by_verdict": {
            "additionalProperties": {
              "type": "integer"
            },
            "propertyNames": {
              "$ref": "#/components/schemas/VerdictClass"
            },
            "title": "By Verdict",
            "type": "object"
          },
          "cancelled": {
            "title": "Cancelled",
            "type": "integer"
          },
          "date": {
            "title": "Date",
            "type": "string"
          },
          "failed": {
            "title": "Failed",
            "type": "integer"
          },
          "median_ms": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Median Ms"
          },
          "p95_ms": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "P95 Ms"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "date",
          "total",
          "failed",
          "cancelled",
          "by_verdict",
          "median_ms",
          "p95_ms"
        ],
        "title": "DailyAnalysisStats",
        "type": "object"
      },
      "DailyAnalysisStatsResponse": {
        "description": "Per-day analysis statistics, oldest day first.",
        "properties": {
          "days": {
            "title": "Days",
            "type": "integer"
          },
          "series": {
            "items": {
              "$ref": "#/components/schemas/DailyAnalysisStats"
            },
            "title": "Series",
            "type": "array"
          }
        },
        "required": [
          "days",
          "series"
        ],
        "title": "DailyAnalysisStatsResponse",
        "type": "object"
      },
      "Dataset": {
        "description": "A dataset and its counts of emails: phishing (malicious or suspicious) or legitimate.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "created_by": {
            "title": "Created By",
            "type": "string"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "emails": {
            "title": "Emails",
            "type": "integer"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "legitimate": {
            "title": "Legitimate",
            "type": "integer"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "phishing": {
            "title": "Phishing",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "created_by",
          "created_at",
          "emails",
          "phishing",
          "legitimate"
        ],
        "title": "Dataset",
        "type": "object"
      },
      "DatasetDeleted": {
        "description": "Outcome of a dataset's deletion.",
        "properties": {
          "emails": {
            "title": "Emails",
            "type": "integer"
          }
        },
        "required": [
          "emails"
        ],
        "title": "DatasetDeleted",
        "type": "object"
      },
      "DatasetDetail": {
        "description": "A dataset, with a page of its copies, newest first.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "created_by": {
            "title": "Created By",
            "type": "string"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "emails": {
            "title": "Emails",
            "type": "integer"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "legitimate": {
            "title": "Legitimate",
            "type": "integer"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "members": {
            "items": {
              "$ref": "#/components/schemas/DatasetMemberEntry"
            },
            "title": "Members",
            "type": "array"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "phishing": {
            "title": "Phishing",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "created_by",
          "created_at",
          "emails",
          "phishing",
          "legitimate",
          "members",
          "limit",
          "offset"
        ],
        "title": "DatasetDetail",
        "type": "object"
      },
      "DatasetFile": {
        "additionalProperties": false,
        "description": "A normalized dataset spot-cli read: its name, the SHA-256 of its emails.jsonl, the emails taken.",
        "properties": {
          "emails": {
            "minimum": 1.0,
            "title": "Emails",
            "type": "integer"
          },
          "name": {
            "pattern": "^[A-Za-z0-9._-]{1,100}$",
            "title": "Name",
            "type": "string"
          },
          "sha256": {
            "pattern": "^[0-9a-f]{64}$",
            "title": "Sha256",
            "type": "string"
          }
        },
        "required": [
          "name",
          "sha256",
          "emails"
        ],
        "title": "DatasetFile",
        "type": "object"
      },
      "DatasetIn": {
        "description": "A dataset to create. Its id is a path segment.",
        "properties": {
          "description": {
            "default": "",
            "title": "Description",
            "type": "string"
          },
          "id": {
            "description": "Lower-case letters, digits, - and _",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "title": "Id",
            "type": "string"
          },
          "name": {
            "maxLength": 200,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "title": "DatasetIn",
        "type": "object"
      },
      "DatasetMember": {
        "description": "A copy in a dataset, with the class its label gives it.",
        "properties": {
          "added_at": {
            "format": "date-time",
            "title": "Added At",
            "type": "string"
          },
          "copied_from": {
            "description": "The email it copies, which the purge may have deleted",
            "format": "uuid",
            "title": "Copied From",
            "type": "string"
          },
          "email_id": {
            "description": "The copy's id",
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "labelled_by": {
            "title": "Labelled By",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          }
        },
        "required": [
          "email_id",
          "copied_from",
          "verdict",
          "labelled_by",
          "added_at"
        ],
        "title": "DatasetMember",
        "type": "object"
      },
      "DatasetMemberEntry": {
        "description": "A copy, with the subject and sender of the email (None when it has none).",
        "properties": {
          "added_at": {
            "format": "date-time",
            "title": "Added At",
            "type": "string"
          },
          "copied_from": {
            "description": "The email it copies, which the purge may have deleted",
            "format": "uuid",
            "title": "Copied From",
            "type": "string"
          },
          "email_id": {
            "description": "The copy's id",
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "labelled_by": {
            "title": "Labelled By",
            "type": "string"
          },
          "sender": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sender"
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          }
        },
        "required": [
          "email_id",
          "copied_from",
          "verdict",
          "labelled_by",
          "added_at",
          "subject",
          "sender"
        ],
        "title": "DatasetMemberEntry",
        "type": "object"
      },
      "DatasetPage": {
        "description": "A page of datasets, newest first.",
        "properties": {
          "datasets": {
            "items": {
              "$ref": "#/components/schemas/Dataset"
            },
            "title": "Datasets",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "datasets",
          "total",
          "limit",
          "offset"
        ],
        "title": "DatasetPage",
        "type": "object"
      },
      "DatasetSelection": {
        "additionalProperties": false,
        "description": "Every email of a dataset of promoted copies; the platform runs it.",
        "properties": {
          "dataset": {
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "title": "Dataset",
            "type": "string"
          }
        },
        "required": [
          "dataset"
        ],
        "title": "DatasetSelection",
        "type": "object"
      },
      "DecisiveSignal": {
        "description": "A signal that sets the verdict, whatever the scores say.",
        "properties": {
          "signal": {
            "description": "Exact name, or a prefix ending in \".*\"",
            "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)*\\.([a-z0-9_]+|\\*)$",
            "title": "Signal",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          }
        },
        "required": [
          "signal",
          "verdict"
        ],
        "title": "DecisiveSignal",
        "type": "object"
      },
      "Deleted": {
        "description": "Outcome of a run's deletion.",
        "properties": {
          "deleted": {
            "title": "Deleted",
            "type": "boolean"
          }
        },
        "required": [
          "deleted"
        ],
        "title": "Deleted",
        "type": "object"
      },
      "Egress": {
        "description": "What data a plugin sends outside SPOT; an image without the label counts as content.",
        "enum": [
          "none",
          "indicators",
          "content"
        ],
        "title": "Egress",
        "type": "string"
      },
      "EmailAnalysisStatusWithEmail": {
        "description": "Extended analysis status that includes the email's summary.",
        "properties": {
          "actions": {
            "description": "What was done with the email, or would have been in observe: the retriever's own action and each responder called",
            "items": {
              "$ref": "#/components/schemas/ActionRecord"
            },
            "title": "Actions",
            "type": "array"
          },
          "completed_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completed At"
          },
          "created_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created At"
          },
          "decided_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "\"signal:<name>\", \"method:<name>\", \"required_unknown\", \"all_unknown\", \"failed\", \"cancelled\", or \"v1\" for an analysis converted from contract 1",
            "title": "Decided By"
          },
          "email": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email"
          },
          "email_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email Id"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "job_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Job Id"
          },
          "label": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Label"
              },
              {
                "type": "null"
              }
            ],
            "description": "The analyst's label of the email, None while the job runs; its note is empty for a viewer"
          },
          "mode": {
            "anyOf": [
              {
                "enum": [
                  "observe",
                  "enforce"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "enforce only when the email's connection and route both are; a user's submission is observe. None for an analysis from before modes, or a message refused as no analysis job",
            "title": "Mode"
          },
          "result": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/OrchestrationResult"
              },
              {
                "type": "null"
              }
            ],
            "description": "The workflow's result, with every analyzer's answer; None while the job runs, for a failed job and for an analysis converted from contract 1"
          },
          "route": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The route that chose the workflow; None for a workflow named by the user, the default, or an analysis from before routing",
            "title": "Route"
          },
          "score": {
            "anyOf": [
              {
                "maximum": 100.0,
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "0-100, how likely the email is malicious",
            "title": "Score"
          },
          "signals": {
            "description": "The signals behind the verdict, strongest first",
            "items": {
              "$ref": "#/components/schemas/AttributedSignal"
            },
            "title": "Signals",
            "type": "array"
          },
          "started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Started At"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status"
          },
          "verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ],
            "description": "None while the job runs"
          },
          "workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ID of the workflow used",
            "title": "Workflow Id"
          }
        },
        "title": "EmailAnalysisStatusWithEmail",
        "type": "object"
      },
      "EmailDeleteResponse": {
        "description": "Outcome of an email deletion.",
        "properties": {
          "deleted": {
            "title": "Deleted",
            "type": "boolean"
          },
          "message": {
            "title": "Message",
            "type": "string"
          }
        },
        "required": [
          "deleted",
          "message"
        ],
        "title": "EmailDeleteResponse",
        "type": "object"
      },
      "EmailListResponse": {
        "description": "Paginated list of stored emails.",
        "properties": {
          "emails": {
            "items": {
              "$ref": "#/components/schemas/EmailSummary"
            },
            "title": "Emails",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "emails",
          "total",
          "limit",
          "offset"
        ],
        "title": "EmailListResponse",
        "type": "object"
      },
      "EmailSourcesResponse": {
        "description": "Distinct sources of the stored emails.",
        "properties": {
          "sources": {
            "items": {
              "type": "string"
            },
            "title": "Sources",
            "type": "array"
          }
        },
        "required": [
          "sources"
        ],
        "title": "EmailSourcesResponse",
        "type": "object"
      },
      "EmailSummary": {
        "description": "A stored email as lists name it: never its view or its body.",
        "properties": {
          "analyzed_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Analyzed At"
          },
          "created_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created At"
          },
          "expires_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "original_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "A label, for emails stored before 2.0",
            "title": "Original Id"
          },
          "sender": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sender"
          },
          "size_bytes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Size of the raw message; None for an email stored before 2.0, which has none",
            "title": "Size Bytes"
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "subject": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Subject"
          }
        },
        "required": [
          "id",
          "original_id",
          "subject",
          "sender",
          "source",
          "size_bytes",
          "created_at",
          "analyzed_at",
          "expires_at"
        ],
        "title": "EmailSummary",
        "type": "object"
      },
      "EmailView": {
        "description": "An email as analyzers see it: headers, parts, bodies and URLs.",
        "properties": {
          "anomalies": {
            "description": "What the parser read past, and where",
            "items": {
              "$ref": "#/components/schemas/Anomaly"
            },
            "title": "Anomalies",
            "type": "array"
          },
          "authentication": {
            "description": "Trusted authserv-ids only",
            "items": {
              "$ref": "#/components/schemas/AuthResult"
            },
            "title": "Authentication",
            "type": "array"
          },
          "cc": {
            "items": {
              "$ref": "#/components/schemas/Address"
            },
            "title": "Cc",
            "type": "array"
          },
          "date": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Date"
          },
          "envelope": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Envelope"
              },
              {
                "type": "null"
              }
            ]
          },
          "from": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "type": "null"
              }
            ]
          },
          "headers": {
            "description": "The headers in order, within their budget, X-SPOT-* removed; those left out are noted as header_limit",
            "items": {
              "$ref": "#/components/schemas/Header"
            },
            "title": "Headers",
            "type": "array"
          },
          "html": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "First text/html body part, decoded",
            "title": "Html"
          },
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "in_reply_to": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "In Reply To"
          },
          "message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Message Id"
          },
          "parts": {
            "items": {
              "$ref": "#/components/schemas/Part"
            },
            "title": "Parts",
            "type": "array"
          },
          "received_at": {
            "format": "date-time",
            "title": "Received At",
            "type": "string"
          },
          "references": {
            "items": {
              "type": "string"
            },
            "title": "References",
            "type": "array"
          },
          "reply_to": {
            "items": {
              "$ref": "#/components/schemas/Address"
            },
            "title": "Reply To",
            "type": "array"
          },
          "sha256": {
            "description": "Of the raw message",
            "title": "Sha256",
            "type": "string"
          },
          "size_bytes": {
            "title": "Size Bytes",
            "type": "integer"
          },
          "source": {
            "description": "Retriever id, or \"api\"",
            "title": "Source",
            "type": "string"
          },
          "subject": {
            "default": "",
            "title": "Subject",
            "type": "string"
          },
          "text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "First text/plain body part, decoded",
            "title": "Text"
          },
          "to": {
            "items": {
              "$ref": "#/components/schemas/Address"
            },
            "title": "To",
            "type": "array"
          },
          "truncated": {
            "default": false,
            "description": "Some content was cut at a limit",
            "title": "Truncated",
            "type": "boolean"
          },
          "urls": {
            "items": {
              "$ref": "#/components/schemas/Url"
            },
            "title": "Urls",
            "type": "array"
          },
          "visible_text": {
            "default": "",
            "description": "text, else the HTML's visible text; without the characters that render as nothing (spot_sdk.mime.visible)",
            "title": "Visible Text",
            "type": "string"
          }
        },
        "required": [
          "id",
          "received_at",
          "source",
          "size_bytes",
          "sha256",
          "headers"
        ],
        "title": "EmailView",
        "type": "object"
      },
      "Envelope": {
        "description": "The SMTP envelope, when the message came through an MTA.",
        "properties": {
          "client_address": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Address"
          },
          "client_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Client Name"
          },
          "helo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Helo"
          },
          "mail_from": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mail From"
          },
          "rcpt_to": {
            "items": {
              "type": "string"
            },
            "title": "Rcpt To",
            "type": "array"
          }
        },
        "title": "Envelope",
        "type": "object"
      },
      "ErrorResponse": {
        "description": "The body of every error the gateway answers.",
        "properties": {
          "details": {
            "additionalProperties": true,
            "description": "More about the error, e.g. the limit",
            "title": "Details",
            "type": "object"
          },
          "error": {
            "description": "A stable code: not_found, parse_limit, validation_error, ...",
            "title": "Error",
            "type": "string"
          },
          "message": {
            "description": "What went wrong, never the content of a message",
            "title": "Message",
            "type": "string"
          }
        },
        "required": [
          "error",
          "message",
          "details"
        ],
        "title": "ErrorResponse",
        "type": "object"
      },
      "FailureStrategy": {
        "description": "Strategy for handling analyzer failures.",
        "enum": [
          "retry",
          "skip",
          "fail"
        ],
        "title": "FailureStrategy",
        "type": "string"
      },
      "FileSelection": {
        "additionalProperties": false,
        "description": "The emails spot-cli takes from its normalized datasets; it submits them and reports their results.",
        "properties": {
          "files": {
            "items": {
              "$ref": "#/components/schemas/DatasetFile"
            },
            "maxItems": 100,
            "minItems": 1,
            "title": "Files",
            "type": "array"
          },
          "label": {
            "anyOf": [
              {
                "enum": [
                  "phishing",
                  "legitimate"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "max_emails": {
            "anyOf": [
              {
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Emails"
          },
          "seed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seed"
          },
          "shuffle": {
            "default": false,
            "title": "Shuffle",
            "type": "boolean"
          },
          "total": {
            "description": "The emails it submits",
            "maximum": 50000.0,
            "minimum": 1.0,
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "files",
          "total"
        ],
        "title": "FileSelection",
        "type": "object"
      },
      "GitRepository": {
        "description": "Where a plugin's code lives, as SPOT reads it: the forge, its URL, the repository's path.",
        "properties": {
          "forge": {
            "description": "The forge SPOT reads it with: gitlab, or gitea for Gitea, Forgejo and Codeberg",
            "enum": [
              "gitlab",
              "gitea"
            ],
            "title": "Forge",
            "type": "string"
          },
          "path": {
            "description": "The repository's path there, owner and name segments such as SPOT_Project/analyzer-rules",
            "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]*(/[A-Za-z0-9_][A-Za-z0-9_.-]*)+$",
            "title": "Path",
            "type": "string"
          },
          "url": {
            "description": "The forge's base URL",
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "forge",
          "url",
          "path"
        ],
        "title": "GitRepository",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "Header": {
        "description": "One header field.",
        "properties": {
          "name": {
            "description": "As written",
            "title": "Name",
            "type": "string"
          },
          "value": {
            "description": "Unfolded, RFC 2047 decoded; undecodable words kept as-is",
            "title": "Value",
            "type": "string"
          }
        },
        "required": [
          "name",
          "value"
        ],
        "title": "Header",
        "type": "object"
      },
      "HealthResponse": {
        "description": "Health check response model.",
        "properties": {
          "service": {
            "description": "Service name",
            "title": "Service",
            "type": "string"
          },
          "status": {
            "description": "Service status",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "service"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "IngestionRequest": {
        "description": "The body of ``POST /internal/ingest``.",
        "properties": {
          "envelope": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Envelope"
              },
              {
                "type": "null"
              }
            ]
          },
          "priority": {
            "default": "normal",
            "enum": [
              "low",
              "normal",
              "high"
            ],
            "title": "Priority",
            "type": "string"
          },
          "raw": {
            "description": "The RFC 5322 message",
            "format": "base64",
            "title": "Raw",
            "type": "string"
          },
          "timeout_ms": {
            "default": 30000,
            "description": "The caller's budget when wait_for_verdict is set",
            "maximum": 300000.0,
            "minimum": 1000.0,
            "title": "Timeout Ms",
            "type": "integer"
          },
          "wait_for_verdict": {
            "default": false,
            "title": "Wait For Verdict",
            "type": "boolean"
          }
        },
        "required": [
          "raw"
        ],
        "title": "IngestionRequest",
        "type": "object"
      },
      "IngestionResult": {
        "description": "The response of ``POST /internal/ingest`` for a stored message.",
        "properties": {
          "email_id": {
            "description": "The stored email",
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why there is no verdict, when the status is timeout",
            "title": "Error"
          },
          "job_id": {
            "title": "Job Id",
            "type": "string"
          },
          "mode": {
            "default": "observe",
            "description": "The job's mode: enforce only when its connection and its route both are; a retriever applies its actions only in enforce",
            "enum": [
              "observe",
              "enforce"
            ],
            "title": "Mode",
            "type": "string"
          },
          "score": {
            "anyOf": [
              {
                "maximum": 100.0,
                "minimum": 0.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set when the status is analyzed",
            "title": "Score"
          },
          "status": {
            "$ref": "#/components/schemas/IngestionStatus"
          },
          "verdict": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VerdictClass"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set when the status is analyzed"
          }
        },
        "required": [
          "job_id",
          "email_id",
          "status"
        ],
        "title": "IngestionResult",
        "type": "object"
      },
      "IngestionStatus": {
        "description": "Terminal state of an ingestion call.",
        "enum": [
          "accepted",
          "analyzed",
          "timeout"
        ],
        "title": "IngestionStatus",
        "type": "string"
      },
      "InstallResultResponse": {
        "description": "Response model for plugin installation.",
        "properties": {
          "container_id": {
            "title": "Container Id",
            "type": "string"
          },
          "container_name": {
            "title": "Container Name",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "internal_url": {
            "title": "Internal Url",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind",
            "description": "The plugin's kind: spot.yaml stores it under that kind's section"
          },
          "message": {
            "title": "Message",
            "type": "string"
          },
          "version": {
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "id",
          "container_id",
          "container_name",
          "version",
          "internal_url",
          "message"
        ],
        "title": "InstallResultResponse",
        "type": "object"
      },
      "InstalledListResponse": {
        "description": "Response model for list of installed plugins.",
        "properties": {
          "plugins": {
            "items": {
              "$ref": "#/components/schemas/InstalledPluginResponse"
            },
            "title": "Plugins",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "plugins",
          "total"
        ],
        "title": "InstalledListResponse",
        "type": "object"
      },
      "InstalledPluginResponse": {
        "description": "Response model for an installed plugin.",
        "properties": {
          "consent": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Consent"
              },
              {
                "type": "null"
              }
            ],
            "description": "The admin's consent to the plugin's egress, when it needs one"
          },
          "container_id": {
            "title": "Container Id",
            "type": "string"
          },
          "container_name": {
            "title": "Container Name",
            "type": "string"
          },
          "effective_resources": {
            "$ref": "#/components/schemas/Resources",
            "description": "The limits the container runs with: the override, else the image's request, else the default"
          },
          "egress": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Egress"
              },
              {
                "type": "null"
              }
            ],
            "description": "The installed image's egress class; None when not recorded"
          },
          "enabled": {
            "title": "Enabled",
            "type": "boolean"
          },
          "health": {
            "description": "Health check status",
            "title": "Health",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "image": {
            "title": "Image",
            "type": "string"
          },
          "installed_at": {
            "format": "date-time",
            "title": "Installed At",
            "type": "string"
          },
          "internal_url": {
            "title": "Internal Url",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind",
            "description": "The plugin's kind: spot.yaml stores it under that kind's section"
          },
          "latest_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The version the catalog lists for this plugin's source: the newest this SPOT runs. None when the plugin is no longer in the catalog (source removed or plugin de-listed).",
            "title": "Latest Version"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "plugin_health": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PluginState"
              },
              {
                "type": "null"
              }
            ],
            "description": "The plugin's own state, from the body of its /health, beside its container's status and health; None in the answer to a settings change, which restarts the plugin"
          },
          "previous_version": {
            "default": "",
            "description": "The version the last update replaced, which a roll back installs",
            "title": "Previous Version",
            "type": "string"
          },
          "reach": {
            "description": "What the plugin reaches through the egress gateway, as host:port (#143)",
            "items": {
              "type": "string"
            },
            "title": "Reach",
            "type": "array"
          },
          "requested_resources": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Resources"
              },
              {
                "type": "null"
              }
            ],
            "description": "The memory and CPU the image asked for; None when it did not"
          },
          "resources": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Resources"
              },
              {
                "type": "null"
              }
            ],
            "description": "The memory and CPU override set by an admin; None when unset"
          },
          "restarting": {
            "default": false,
            "description": "True when saving settings, secrets or resources re-creates the plugin's container: it is unavailable for a few seconds",
            "title": "Restarting",
            "type": "boolean"
          },
          "secrets": {
            "description": "Names of the plugin's secret settings, never the values",
            "items": {
              "type": "string"
            },
            "title": "Secrets",
            "type": "array"
          },
          "settings": {
            "additionalProperties": true,
            "title": "Settings",
            "type": "object"
          },
          "source_id": {
            "default": "",
            "description": "Source the plugin was installed from (empty for manual entries).",
            "title": "Source Id",
            "type": "string"
          },
          "source_name": {
            "default": "",
            "description": "Human-readable source name (empty for manual entries).",
            "title": "Source Name",
            "type": "string"
          },
          "status": {
            "description": "Container status (running, stopped, etc.)",
            "title": "Status",
            "type": "string"
          },
          "update_available": {
            "default": false,
            "description": "True when ``latest_version`` is known and newer than the installed ``version``.",
            "title": "Update Available",
            "type": "boolean"
          },
          "version": {
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "id",
          "name",
          "container_id",
          "container_name",
          "image",
          "version",
          "installed_at",
          "settings",
          "enabled",
          "internal_url",
          "status",
          "health",
          "effective_resources"
        ],
        "title": "InstalledPluginResponse",
        "type": "object"
      },
      "Label": {
        "description": "An analyst's verdict on an email.",
        "properties": {
          "analysis_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Analysis Id"
          },
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "labelled_at": {
            "format": "date-time",
            "title": "Labelled At",
            "type": "string"
          },
          "labelled_by": {
            "title": "Labelled By",
            "type": "string"
          },
          "note": {
            "title": "Note",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass",
            "description": "Never unknown"
          }
        },
        "required": [
          "email_id",
          "verdict",
          "note",
          "analysis_id",
          "labelled_by",
          "labelled_at"
        ],
        "title": "Label",
        "type": "object"
      },
      "LabelEntry": {
        "description": "A label, with the sender of its email (None when the email has none).",
        "properties": {
          "analysis_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Analysis Id"
          },
          "dataset_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The email's dataset when it is a dataset's copy; None for a received email",
            "title": "Dataset Id"
          },
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "labelled_at": {
            "format": "date-time",
            "title": "Labelled At",
            "type": "string"
          },
          "labelled_by": {
            "title": "Labelled By",
            "type": "string"
          },
          "note": {
            "title": "Note",
            "type": "string"
          },
          "sender": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sender"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass",
            "description": "Never unknown"
          }
        },
        "required": [
          "email_id",
          "verdict",
          "note",
          "analysis_id",
          "labelled_by",
          "labelled_at",
          "sender",
          "dataset_id"
        ],
        "title": "LabelEntry",
        "type": "object"
      },
      "LabelIn": {
        "description": "A label to set: the class, a note, and the analysis it corrects.",
        "properties": {
          "analysis_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The analysis the label corrects, if it was made on one",
            "title": "Analysis Id"
          },
          "note": {
            "default": "",
            "maxLength": 2000,
            "title": "Note",
            "type": "string"
          },
          "verdict": {
            "enum": [
              "malicious",
              "suspicious",
              "spam",
              "benign"
            ],
            "title": "Verdict",
            "type": "string"
          }
        },
        "required": [
          "verdict"
        ],
        "title": "LabelIn",
        "type": "object"
      },
      "LabelPage": {
        "description": "A page of labels, newest first.",
        "properties": {
          "labels": {
            "items": {
              "$ref": "#/components/schemas/LabelEntry"
            },
            "title": "Labels",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "labels",
          "total",
          "limit",
          "offset"
        ],
        "title": "LabelPage",
        "type": "object"
      },
      "Latency": {
        "description": "Nearest-rank percentiles of some durations; None without any.",
        "properties": {
          "median_ms": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Median Ms"
          },
          "p95_ms": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "P95 Ms"
          }
        },
        "title": "Latency",
        "type": "object"
      },
      "MailFilter": {
        "additionalProperties": false,
        "description": "Stored mail received in a period: the most recent first, up to ``limit``.",
        "properties": {
          "labelled_only": {
            "default": false,
            "description": "Only the emails an analyst labelled",
            "title": "Labelled Only",
            "type": "boolean"
          },
          "limit": {
            "default": 500,
            "maximum": 10000.0,
            "minimum": 1.0,
            "title": "Limit",
            "type": "integer"
          },
          "since": {
            "format": "date-time",
            "title": "Since",
            "type": "string"
          },
          "sources": {
            "description": "Empty: every source",
            "items": {
              "maxLength": 255,
              "type": "string"
            },
            "maxItems": 50,
            "title": "Sources",
            "type": "array"
          },
          "until": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "None: now, which the run records as its end",
            "title": "Until"
          }
        },
        "required": [
          "since"
        ],
        "title": "MailFilter",
        "type": "object"
      },
      "MailSelection": {
        "additionalProperties": false,
        "description": "A backtest over the stored mail; the platform runs it.",
        "properties": {
          "mail": {
            "$ref": "#/components/schemas/MailFilter"
          }
        },
        "required": [
          "mail"
        ],
        "title": "MailSelection",
        "type": "object"
      },
      "MessageResponse": {
        "description": "Generic message response.",
        "properties": {
          "message": {
            "title": "Message",
            "type": "string"
          }
        },
        "required": [
          "message"
        ],
        "title": "MessageResponse",
        "type": "object"
      },
      "Metrics": {
        "additionalProperties": false,
        "description": "The counts, confusion matrix, rates and mean score of some emails.\n\n``completed`` counts the emails in the confusion matrix, ``unknown`` the\ncompleted emails whose verdict is unknown, and ``failed`` the others\n(failed, timed out, not submitted).",
        "properties": {
          "accuracy": {
            "default": 0.0,
            "title": "Accuracy",
            "type": "number"
          },
          "avg_score": {
            "default": 0.0,
            "title": "Avg Score",
            "type": "number"
          },
          "completed": {
            "default": 0,
            "title": "Completed",
            "type": "integer"
          },
          "f1": {
            "default": 0.0,
            "title": "F1",
            "type": "number"
          },
          "f2": {
            "default": 0.0,
            "title": "F2",
            "type": "number"
          },
          "failed": {
            "default": 0,
            "title": "Failed",
            "type": "integer"
          },
          "fn": {
            "default": 0,
            "title": "Fn",
            "type": "integer"
          },
          "fnr": {
            "default": 0.0,
            "title": "Fnr",
            "type": "number"
          },
          "fp": {
            "default": 0,
            "title": "Fp",
            "type": "integer"
          },
          "fpr": {
            "default": 0.0,
            "title": "Fpr",
            "type": "number"
          },
          "fpr_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fpr Ci"
          },
          "precision": {
            "default": 0.0,
            "title": "Precision",
            "type": "number"
          },
          "precision_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Precision Ci"
          },
          "recall": {
            "default": 0.0,
            "title": "Recall",
            "type": "number"
          },
          "recall_ci": {
            "anyOf": [
              {
                "maxItems": 2,
                "minItems": 2,
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Recall Ci"
          },
          "tn": {
            "default": 0,
            "title": "Tn",
            "type": "integer"
          },
          "total": {
            "default": 0,
            "title": "Total",
            "type": "integer"
          },
          "tp": {
            "default": 0,
            "title": "Tp",
            "type": "integer"
          },
          "unknown": {
            "default": 0,
            "title": "Unknown",
            "type": "integer"
          }
        },
        "title": "Metrics",
        "type": "object"
      },
      "OptionUpdateResponse": {
        "description": "An option as it is after an update.",
        "properties": {
          "default": {
            "description": "Default value from schema",
            "title": "Default"
          },
          "description": {
            "default": "",
            "description": "Human-readable description",
            "title": "Description",
            "type": "string"
          },
          "enum": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Allowed values if restricted",
            "title": "Enum"
          },
          "is_default": {
            "description": "True if current value equals default",
            "title": "Is Default",
            "type": "boolean"
          },
          "maximum": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Maximum value for numbers",
            "title": "Maximum"
          },
          "minimum": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Minimum value for numbers",
            "title": "Minimum"
          },
          "path": {
            "description": "Dot-notation path (e.g., 'platform.log_level')",
            "title": "Path",
            "type": "string"
          },
          "restarting": {
            "default": false,
            "description": "True when saving settings, secrets or resources re-creates the plugin's container: it is unavailable for a few seconds",
            "title": "Restarting",
            "type": "boolean"
          },
          "type": {
            "description": "JSON Schema type (string, boolean, integer, etc.)",
            "title": "Type",
            "type": "string"
          },
          "value": {
            "description": "Current value",
            "title": "Value"
          }
        },
        "required": [
          "path",
          "value",
          "default",
          "type",
          "is_default"
        ],
        "title": "OptionUpdateResponse",
        "type": "object"
      },
      "OrchestrationResult": {
        "description": "The workflow's verdict on an email, and how it was reached.",
        "properties": {
          "analyzed_at": {
            "format": "date-time",
            "title": "Analyzed At",
            "type": "string"
          },
          "analyzers": {
            "items": {
              "$ref": "#/components/schemas/AnalyzerOutcome"
            },
            "title": "Analyzers",
            "type": "array"
          },
          "decided_by": {
            "description": "\"signal:<name>\", \"method:<name>\", \"required_unknown\" or \"all_unknown\"",
            "title": "Decided By",
            "type": "string"
          },
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "policy": {
            "$ref": "#/components/schemas/VerdictPolicy",
            "description": "The one that decided"
          },
          "score": {
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Score",
            "type": "integer"
          },
          "signals": {
            "description": "Strongest first",
            "items": {
              "$ref": "#/components/schemas/AttributedSignal"
            },
            "title": "Signals",
            "type": "array"
          },
          "total_execution_time_ms": {
            "title": "Total Execution Time Ms",
            "type": "integer"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          },
          "workflow_id": {
            "title": "Workflow Id",
            "type": "string"
          },
          "workflow_version": {
            "title": "Workflow Version",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "email_id",
          "workflow_id",
          "workflow_version",
          "verdict",
          "score",
          "decided_by",
          "signals",
          "analyzers",
          "total_execution_time_ms",
          "analyzed_at",
          "policy"
        ],
        "title": "OrchestrationResult",
        "type": "object"
      },
      "Part": {
        "description": "One node of the MIME tree.",
        "properties": {
          "charset": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Charset"
          },
          "content_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Content Id"
          },
          "content_type": {
            "description": "Lower-cased, from the header (default text/plain)",
            "title": "Content Type",
            "type": "string"
          },
          "disposition": {
            "anyOf": [
              {
                "enum": [
                  "inline",
                  "attachment"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Disposition"
          },
          "filename": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Filename"
          },
          "part_id": {
            "description": "\"1\", \"1.2\", \"1.2.1\": the MIME tree, depth-first",
            "title": "Part Id",
            "type": "string"
          },
          "sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Of the decoded bytes; None for containers (multipart/* and message/*)",
            "title": "Sha256"
          },
          "size_bytes": {
            "description": "Decoded size; 0 for containers (multipart/* and message/*)",
            "title": "Size Bytes",
            "type": "integer"
          }
        },
        "required": [
          "part_id",
          "content_type",
          "charset",
          "disposition",
          "filename",
          "content_id",
          "size_bytes",
          "sha256"
        ],
        "title": "Part",
        "type": "object"
      },
      "PlatformConfigResponse": {
        "properties": {
          "access_token_expire_minutes": {
            "title": "Access Token Expire Minutes",
            "type": "integer"
          },
          "allowed_hosts": {
            "items": {
              "type": "string"
            },
            "title": "Allowed Hosts",
            "type": "array"
          },
          "allowed_origins": {
            "items": {
              "type": "string"
            },
            "title": "Allowed Origins",
            "type": "array"
          },
          "audit_retention_days": {
            "title": "Audit Retention Days",
            "type": "integer"
          },
          "config_version": {
            "title": "Config Version",
            "type": "string"
          },
          "data_retention_days": {
            "title": "Data Retention Days",
            "type": "integer"
          },
          "debug": {
            "title": "Debug",
            "type": "boolean"
          },
          "enable_oauth": {
            "title": "Enable Oauth",
            "type": "boolean"
          },
          "enable_rate_limiting": {
            "title": "Enable Rate Limiting",
            "type": "boolean"
          },
          "log_level": {
            "title": "Log Level",
            "type": "string"
          },
          "mail_orchestrator_timeout": {
            "title": "Mail Orchestrator Timeout",
            "type": "integer"
          },
          "orchestrator_timeout": {
            "title": "Orchestrator Timeout",
            "type": "integer"
          },
          "plugin_rate_limit_requests": {
            "title": "Plugin Rate Limit Requests",
            "type": "integer"
          },
          "rate_limit_period": {
            "title": "Rate Limit Period",
            "type": "integer"
          },
          "rate_limit_requests": {
            "title": "Rate Limit Requests",
            "type": "integer"
          },
          "refresh_token_expire_days": {
            "title": "Refresh Token Expire Days",
            "type": "integer"
          }
        },
        "required": [
          "log_level",
          "debug",
          "orchestrator_timeout",
          "mail_orchestrator_timeout",
          "access_token_expire_minutes",
          "refresh_token_expire_days",
          "enable_rate_limiting",
          "rate_limit_requests",
          "rate_limit_period",
          "plugin_rate_limit_requests",
          "enable_oauth",
          "data_retention_days",
          "audit_retention_days",
          "allowed_origins",
          "allowed_hosts",
          "config_version"
        ],
        "title": "PlatformConfigResponse",
        "type": "object"
      },
      "PlatformConfigUpdateRequest": {
        "properties": {
          "access_token_expire_minutes": {
            "anyOf": [
              {
                "maximum": 1440.0,
                "minimum": 5.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Access Token Expire Minutes"
          },
          "allowed_hosts": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Allowed Hosts"
          },
          "allowed_origins": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Allowed Origins"
          },
          "audit_retention_days": {
            "anyOf": [
              {
                "maximum": 3650.0,
                "minimum": 30.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Audit Retention Days"
          },
          "data_retention_days": {
            "anyOf": [
              {
                "maximum": 36500.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Data Retention Days"
          },
          "debug": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "New debug state",
            "title": "Debug"
          },
          "enable_oauth": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enable Oauth"
          },
          "enable_rate_limiting": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enable Rate Limiting"
          },
          "log_level": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New log level",
            "title": "Log Level"
          },
          "mail_orchestrator_timeout": {
            "anyOf": [
              {
                "maximum": 300.0,
                "minimum": 5.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mail Orchestrator Timeout"
          },
          "orchestrator_timeout": {
            "anyOf": [
              {
                "maximum": 300.0,
                "minimum": 5.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Orchestrator Timeout"
          },
          "plugin_rate_limit_requests": {
            "anyOf": [
              {
                "maximum": 100000.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Plugin Rate Limit Requests"
          },
          "rate_limit_period": {
            "anyOf": [
              {
                "maximum": 3600.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rate Limit Period"
          },
          "rate_limit_requests": {
            "anyOf": [
              {
                "maximum": 10000.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rate Limit Requests"
          },
          "refresh_token_expire_days": {
            "anyOf": [
              {
                "maximum": 30.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Refresh Token Expire Days"
          }
        },
        "title": "PlatformConfigUpdateRequest",
        "type": "object"
      },
      "PluginConfigCreateRequest": {
        "properties": {
          "enabled": {
            "default": true,
            "description": "Whether the plugin is enabled",
            "title": "Enabled",
            "type": "boolean"
          },
          "id": {
            "description": "Unique plugin identifier",
            "title": "Id",
            "type": "string"
          },
          "secrets": {
            "additionalProperties": true,
            "description": "Secret settings by name, sealed in spot.yaml and delivered as files, never returned. Settings the plugin's schema marks secret join them. An empty value keeps the stored one.",
            "title": "Secrets",
            "type": "object"
          },
          "settings": {
            "additionalProperties": true,
            "description": "Plugin-specific settings",
            "title": "Settings",
            "type": "object"
          },
          "url": {
            "default": "",
            "description": "HTTP URL to reach the plugin",
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "PluginConfigCreateRequest",
        "type": "object"
      },
      "PluginConfigResponse": {
        "description": "A plugin's entry of spot.yaml: ``kind`` is its section.",
        "properties": {
          "config_version": {
            "title": "Config Version",
            "type": "string"
          },
          "consent": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Consent"
              },
              {
                "type": "null"
              }
            ]
          },
          "container_id": {
            "default": "",
            "title": "Container Id",
            "type": "string"
          },
          "container_name": {
            "default": "",
            "title": "Container Name",
            "type": "string"
          },
          "egress": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Egress"
              },
              {
                "type": "null"
              }
            ]
          },
          "enabled": {
            "title": "Enabled",
            "type": "boolean"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "image": {
            "default": "",
            "title": "Image",
            "type": "string"
          },
          "installed_at": {
            "default": "",
            "title": "Installed At",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind",
            "description": "The plugin's kind: spot.yaml stores it under that kind's section"
          },
          "mode": {
            "anyOf": [
              {
                "enum": [
                  "observe",
                  "enforce"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "A mail connection's mode, observe when its entry has none; None for a plugin of another kind",
            "title": "Mode"
          },
          "previous_version": {
            "default": "",
            "description": "The version the last update replaced, which a roll back installs; empty before one",
            "title": "Previous Version",
            "type": "string"
          },
          "restarting": {
            "default": false,
            "description": "True when saving settings, secrets or resources re-creates the plugin's container: it is unavailable for a few seconds",
            "title": "Restarting",
            "type": "boolean"
          },
          "secrets": {
            "description": "Names of the plugin's secret settings, never the values",
            "items": {
              "type": "string"
            },
            "title": "Secrets",
            "type": "array"
          },
          "settings": {
            "additionalProperties": true,
            "title": "Settings",
            "type": "object"
          },
          "source_id": {
            "default": "",
            "title": "Source Id",
            "type": "string"
          },
          "source_name": {
            "default": "",
            "title": "Source Name",
            "type": "string"
          },
          "sync_schedule": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sync Schedule"
          },
          "sync_timeout_ms": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sync Timeout Ms"
          },
          "url": {
            "default": "",
            "title": "Url",
            "type": "string"
          },
          "version": {
            "default": "",
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "id",
          "enabled",
          "settings",
          "config_version"
        ],
        "title": "PluginConfigResponse",
        "type": "object"
      },
      "PluginConfigUpdateRequest": {
        "properties": {
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "New enabled state",
            "title": "Enabled"
          },
          "secrets": {
            "additionalProperties": true,
            "description": "Secret settings by name, sealed in spot.yaml and delivered as files, never returned. Settings the plugin's schema marks secret join them. An empty value keeps the stored one.",
            "title": "Secrets",
            "type": "object"
          },
          "settings": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "New settings (replaces existing)",
            "title": "Settings"
          },
          "sync_schedule": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cron schedule or empty to clear",
            "title": "Sync Schedule"
          },
          "sync_timeout_ms": {
            "anyOf": [
              {
                "maximum": 3600000.0,
                "minimum": 1000.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sync Timeout Ms"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New HTTP URL",
            "title": "Url"
          }
        },
        "title": "PluginConfigUpdateRequest",
        "type": "object"
      },
      "PluginInfoResponse": {
        "description": "Response model for plugin info (catalog entry).",
        "properties": {
          "available_versions": {
            "items": {
              "type": "string"
            },
            "title": "Available Versions",
            "type": "array"
          },
          "capabilities": {
            "items": {
              "type": "string"
            },
            "title": "Capabilities",
            "type": "array"
          },
          "compatibility": {
            "title": "Compatibility",
            "type": "string"
          },
          "contract": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "const": "unreadable",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The plugin contract the image declares (spot.plugin.contract): its number, 1 when the label is absent, or 'unreadable'; null for an entry listed by an earlier SPOT, which did not read it",
            "title": "Contract"
          },
          "description": {
            "title": "Description",
            "type": "string"
          },
          "digest": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The sha256 of the image's manifest, which an install pulls: an index entry's; null for another source",
            "title": "Digest"
          },
          "egress": {
            "$ref": "#/components/schemas/Egress",
            "description": "What the plugin sends outside SPOT"
          },
          "git": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GitRepository"
              },
              {
                "type": "null"
              }
            ],
            "description": "The plugin's repository, which its README is read from: as the signed index names it, or as a forge source's scan listed it; null for a registry source"
          },
          "healthcheck_path": {
            "title": "Healthcheck Path",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "image": {
            "title": "Image",
            "type": "string"
          },
          "installed_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Currently installed version",
            "title": "Installed Version"
          },
          "is_compatible": {
            "description": "Compatible with current SPOT version",
            "title": "Is Compatible",
            "type": "boolean"
          },
          "is_installed": {
            "default": false,
            "description": "Whether already installed",
            "title": "Is Installed",
            "type": "boolean"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind",
            "description": "The plugin's kind: spot.yaml stores it under that kind's section"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "port": {
            "title": "Port",
            "type": "integer"
          },
          "publisher": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who publishes the plugin, as the signed index names them; null for another source",
            "title": "Publisher"
          },
          "refusal": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why this SPOT does not install the image; None when it does",
            "title": "Refusal"
          },
          "repo_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Repo Url"
          },
          "requires_external_ingress": {
            "default": false,
            "description": "Plugin needs ingress traffic from outside the SPOT network",
            "title": "Requires External Ingress",
            "type": "boolean"
          },
          "schema_warnings": {
            "description": "Each field of spot.plugin.settings left out, with the rule it broke",
            "items": {
              "type": "string"
            },
            "title": "Schema Warnings",
            "type": "array"
          },
          "settings_schema": {
            "additionalProperties": {
              "$ref": "#/components/schemas/SettingField"
            },
            "title": "Settings Schema",
            "type": "object"
          },
          "signing_key": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The fingerprint (sha256: and the hex of its DER) of the publisher's key, for a verified or community entry of the signed index; null otherwise",
            "title": "Signing Key"
          },
          "source_id": {
            "title": "Source Id",
            "type": "string"
          },
          "source_name": {
            "title": "Source Name",
            "type": "string"
          },
          "tier": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Tier"
              },
              {
                "type": "null"
              }
            ],
            "description": "The plugin's tier in the signed index; null for another source"
          },
          "update_available": {
            "default": false,
            "description": "True when the plugin is installed from this entry's source and the entry's version is newer than the installed version.",
            "title": "Update Available",
            "type": "boolean"
          },
          "version": {
            "description": "The version the catalog lists: the one an install takes, the newest this SPOT runs; the newest scanned when none runs",
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "id",
          "name",
          "description",
          "version",
          "compatibility",
          "is_compatible",
          "capabilities",
          "port",
          "healthcheck_path",
          "settings_schema",
          "egress",
          "contract",
          "refusal",
          "image",
          "source_id",
          "source_name",
          "repo_url",
          "available_versions"
        ],
        "title": "PluginInfoResponse",
        "type": "object"
      },
      "PluginInstallRequest": {
        "description": "Request model for installing a plugin.",
        "properties": {
          "consent_to_content": {
            "default": false,
            "description": "The admin's consent to a plugin of egress class content, which may send email content outside SPOT: required to install it, or to update to it unless already recorded",
            "title": "Consent To Content",
            "type": "boolean"
          },
          "enabled": {
            "default": true,
            "description": "Enable after installation",
            "title": "Enabled",
            "type": "boolean"
          },
          "plugin_id": {
            "description": "Plugin ID to install",
            "title": "Plugin Id",
            "type": "string"
          },
          "secrets": {
            "additionalProperties": true,
            "description": "Secret settings by name, sealed in spot.yaml and delivered as files, never returned. Settings the plugin's schema marks secret join them. An empty value keeps the stored one.",
            "title": "Secrets",
            "type": "object"
          },
          "settings": {
            "additionalProperties": true,
            "description": "Plugin settings",
            "title": "Settings",
            "type": "object"
          },
          "source_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Source to install from. When two sources publish the same plugin id (e.g. an operator's private mirror plus the official Codeberg source), this pins the install to a specific source. Omit to fall back to catalog scan order (priority-first).",
            "title": "Source Id"
          },
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Version to install (default: the newest this SPOT runs)",
            "title": "Version"
          }
        },
        "required": [
          "plugin_id"
        ],
        "title": "PluginInstallRequest",
        "type": "object"
      },
      "PluginKind": {
        "description": "What a plugin does; a plugin has exactly one kind.",
        "enum": [
          "analyzer",
          "context_provider",
          "mail_retriever",
          "responder"
        ],
        "title": "PluginKind",
        "type": "string"
      },
      "PluginModeRequest": {
        "description": "A mail connection's new mode.",
        "properties": {
          "mode": {
            "description": "observe records what the connection would do; enforce does it, on the jobs whose route is in enforce too",
            "enum": [
              "observe",
              "enforce"
            ],
            "title": "Mode",
            "type": "string"
          }
        },
        "required": [
          "mode"
        ],
        "title": "PluginModeRequest",
        "type": "object"
      },
      "PluginReadme": {
        "description": "A plugin's README.md, as its repository holds it at its version's tag (spot/spot#133).",
        "properties": {
          "base_url": {
            "description": "The repository's page at the tag, which relative links resolve against",
            "title": "Base Url",
            "type": "string"
          },
          "markdown": {
            "description": "The file as its authors wrote it: untrusted, rendered as such",
            "title": "Markdown",
            "type": "string"
          },
          "url": {
            "description": "The file's page on its forge",
            "title": "Url",
            "type": "string"
          },
          "version": {
            "description": "The version whose tag it was read at, v<version>",
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "markdown",
          "url",
          "base_url",
          "version"
        ],
        "title": "PluginReadme",
        "type": "object"
      },
      "PluginResourcesRequest": {
        "additionalProperties": false,
        "description": "Request model for overriding an installed plugin's memory and CPUs.\n\nA value left out keeps the plugin's effective one. Any other key is refused.",
        "properties": {
          "cpus": {
            "anyOf": [
              {
                "exclusiveMinimum": 0.0,
                "maximum": 64.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "CPU limit, in CPUs",
            "title": "Cpus"
          },
          "memory_mb": {
            "anyOf": [
              {
                "minimum": 128.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Memory limit, in MB",
            "title": "Memory Mb"
          }
        },
        "title": "PluginResourcesRequest",
        "type": "object"
      },
      "PluginSettingsUpdateRequest": {
        "additionalProperties": false,
        "description": "Request model for changing an installed plugin's settings.\n\nAny other key is refused: a bare settings object, the 2.0.0-beta.3\nbody, would otherwise change nothing and still succeed.",
        "properties": {
          "secrets": {
            "additionalProperties": true,
            "description": "Secret settings by name, sealed in spot.yaml and delivered as files, never returned. Settings the plugin's schema marks secret join them. An empty value keeps the stored one.",
            "title": "Secrets",
            "type": "object"
          },
          "settings": {
            "additionalProperties": true,
            "description": "Settings to change; the others are kept",
            "title": "Settings",
            "type": "object"
          }
        },
        "title": "PluginSettingsUpdateRequest",
        "type": "object"
      },
      "PluginState": {
        "description": "A plugin's own state, from the body of its ``/health``, as the analyzer-orchestrator reads it.\n\n``healthy``, ``starting`` or ``unhealthy``, with the plugin's reason, as the\nSDK's ``PluginHealth`` holds them, and the version it runs; ``unknown``, with\na fixed reason, when the plugin cannot be reached, does not answer in time,\nor answers something else. The reason and the version are the plugin's\ntext: short, and printable (no control, format or separator character),\nto be shown as written.",
        "properties": {
          "reason": {
            "anyOf": [
              {
                "maxLength": 200,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why, in a few words; None without one",
            "title": "Reason"
          },
          "status": {
            "description": "The plugin's own state; unknown when it cannot be read",
            "enum": [
              "healthy",
              "starting",
              "unhealthy",
              "unknown"
            ],
            "title": "Status",
            "type": "string"
          },
          "version": {
            "anyOf": [
              {
                "maxLength": 64,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The version the plugin runs; None when not read",
            "title": "Version"
          }
        },
        "required": [
          "status"
        ],
        "title": "PluginState",
        "type": "object"
      },
      "PluginStatusResponse": {
        "description": "A plugin's container state, and the plugin's own state, of any entry of spot.yaml.",
        "properties": {
          "container_id": {
            "description": "Docker container ID; empty for a manual entry",
            "title": "Container Id",
            "type": "string"
          },
          "container_name": {
            "description": "Docker container name; empty for a manual entry",
            "title": "Container Name",
            "type": "string"
          },
          "health": {
            "description": "The container's Docker health status, or unknown",
            "title": "Health",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "internal_url": {
            "description": "The URL core reaches the plugin at",
            "title": "Internal Url",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind",
            "description": "The plugin's kind"
          },
          "plugin_health": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PluginState"
              },
              {
                "type": "null"
              }
            ],
            "description": "The plugin's own state, from the body of its /health, beside its container's status and health; None for a manual entry without a URL"
          },
          "status": {
            "description": "Container status (running, exited, etc.); not_found when the container is missing, external for a manual entry, which has none",
            "title": "Status",
            "type": "string"
          },
          "version": {
            "description": "Installed version; empty for a manual entry",
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "id",
          "kind",
          "container_id",
          "container_name",
          "version",
          "internal_url",
          "status",
          "health",
          "plugin_health"
        ],
        "title": "PluginStatusResponse",
        "type": "object"
      },
      "PluginTokenResponse": {
        "description": "A plugin's new token, shown once.",
        "properties": {
          "plugin_id": {
            "title": "Plugin Id",
            "type": "string"
          },
          "token": {
            "title": "Token",
            "type": "string"
          }
        },
        "required": [
          "plugin_id",
          "token"
        ],
        "title": "PluginTokenResponse",
        "type": "object"
      },
      "PluginUpdateRequest": {
        "description": "Request model for updating an installed plugin.",
        "properties": {
          "consent_to_content": {
            "default": false,
            "description": "The admin's consent to a plugin of egress class content, which may send email content outside SPOT: required to install it, or to update to it unless already recorded",
            "title": "Consent To Content",
            "type": "boolean"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Enable/disable the plugin",
            "title": "Enabled"
          },
          "secrets": {
            "additionalProperties": true,
            "description": "Secret settings by name, sealed in spot.yaml and delivered as files, never returned. Settings the plugin's schema marks secret join them. An empty value keeps the stored one.",
            "title": "Secrets",
            "type": "object"
          },
          "settings": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Updated settings",
            "title": "Settings"
          },
          "source_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Update from this source instead of the plugin's own: another source that lists the same plugin, such as the index for a plugin installed from Docker Hub",
            "title": "Source Id"
          },
          "version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Version to install (default: the newest this SPOT runs, when it is newer than the installed one; an older one is a roll back)",
            "title": "Version"
          }
        },
        "title": "PluginUpdateRequest",
        "type": "object"
      },
      "Promotion": {
        "description": "The received email to copy into a dataset.",
        "properties": {
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          }
        },
        "required": [
          "email_id"
        ],
        "title": "Promotion",
        "type": "object"
      },
      "QueueReplayed": {
        "description": "What a replay moved back into its queue.",
        "properties": {
          "queue": {
            "title": "Queue",
            "type": "string"
          },
          "replayed": {
            "description": "Messages moved from the dead-letter queue back into the queue",
            "title": "Replayed",
            "type": "integer"
          }
        },
        "required": [
          "queue",
          "replayed"
        ],
        "title": "QueueReplayed",
        "type": "object"
      },
      "RawMessage": {
        "description": "A message submitted as JSON.",
        "properties": {
          "raw": {
            "description": "The RFC 5322 message, base64-encoded",
            "format": "base64",
            "title": "Raw",
            "type": "string"
          }
        },
        "required": [
          "raw"
        ],
        "title": "RawMessage",
        "type": "object"
      },
      "Recorded": {
        "description": "How far a run is: its emails with a result, of its total.",
        "properties": {
          "recorded": {
            "title": "Recorded",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "recorded",
          "total"
        ],
        "title": "Recorded",
        "type": "object"
      },
      "RefreshRequest": {
        "description": "Token refresh request model.",
        "properties": {
          "refresh_token": {
            "description": "Valid refresh token",
            "title": "Refresh Token",
            "type": "string"
          }
        },
        "required": [
          "refresh_token"
        ],
        "title": "RefreshRequest",
        "type": "object"
      },
      "Resources": {
        "description": "Memory and CPU a plugin container may use.",
        "properties": {
          "cpus": {
            "default": 1.0,
            "description": "CPU cores",
            "exclusiveMinimum": 0.0,
            "maximum": 64.0,
            "title": "Cpus",
            "type": "number"
          },
          "memory_mb": {
            "default": 1024,
            "description": "Memory in MB",
            "minimum": 128.0,
            "title": "Memory Mb",
            "type": "integer"
          }
        },
        "title": "Resources",
        "type": "object"
      },
      "ResultBatch": {
        "additionalProperties": false,
        "description": "Results of a run spot-cli runs: 1000 at most per request.",
        "properties": {
          "results": {
            "items": {
              "$ref": "#/components/schemas/BenchmarkEmailResult-Input"
            },
            "maxItems": 1000,
            "minItems": 1,
            "title": "Results",
            "type": "array"
          }
        },
        "required": [
          "results"
        ],
        "title": "ResultBatch",
        "type": "object"
      },
      "RetrievalLimits": {
        "description": "Operator policy for Knowledge Store retrieval inside a stage.\n\nCaps only — these shrink what an analyzer asks for, never expand it.\nThe orchestrator passes them in ``AnalyzeRequest.retrieval_limits``;\n``KnowledgeClient.for_analysis(request)`` enforces them transparently.",
        "properties": {
          "max_top_k": {
            "anyOf": [
              {
                "maximum": 1000.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cap on top_k requested by any analyzer in this stage",
            "title": "Max Top K"
          },
          "min_score_floor": {
            "anyOf": [
              {
                "maximum": 1.0,
                "minimum": 0.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Raise the min_score floor even if analyzer asked for less",
            "title": "Min Score Floor"
          }
        },
        "title": "RetrievalLimits",
        "type": "object"
      },
      "RetryConfig": {
        "description": "Retry configuration for analyzer execution.",
        "properties": {
          "backoff_ms": {
            "default": 1000,
            "description": "Initial backoff time in milliseconds",
            "maximum": 60000.0,
            "minimum": 100.0,
            "title": "Backoff Ms",
            "type": "integer"
          },
          "exponential_backoff": {
            "default": true,
            "description": "Use exponential backoff",
            "title": "Exponential Backoff",
            "type": "boolean"
          },
          "max_attempts": {
            "default": 3,
            "description": "Maximum number of retry attempts",
            "maximum": 10.0,
            "minimum": 1.0,
            "title": "Max Attempts",
            "type": "integer"
          },
          "max_backoff_ms": {
            "default": 10000,
            "description": "Maximum backoff time in milliseconds",
            "maximum": 300000.0,
            "minimum": 1000.0,
            "title": "Max Backoff Ms",
            "type": "integer"
          }
        },
        "title": "RetryConfig",
        "type": "object"
      },
      "RollbackResponse": {
        "description": "The commit a rollback made, and the sections it changed.",
        "properties": {
          "commit": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CommitSummary"
              },
              {
                "type": "null"
              }
            ],
            "description": "The rollback's commit; null when none was recorded: nothing changed, the history is paused (a start or a reload is sealing secrets, and its commit records the change), or the commit failed (logged; the next commit records the change)"
          },
          "sections": {
            "description": "The top-level sections of spot.yaml it changed, from the settings in force before it, recorded or not",
            "items": {
              "type": "string"
            },
            "title": "Sections",
            "type": "array"
          }
        },
        "required": [
          "commit",
          "sections"
        ],
        "title": "RollbackResponse",
        "type": "object"
      },
      "Route": {
        "additionalProperties": false,
        "description": "A workflow for the emails a match holds for.",
        "properties": {
          "match": {
            "$ref": "#/components/schemas/RouteMatch",
            "description": "Empty: every email"
          },
          "mode": {
            "default": "enforce",
            "description": "enforce follows the connection; observe pins the route",
            "enum": [
              "observe",
              "enforce"
            ],
            "title": "Mode",
            "type": "string"
          },
          "name": {
            "description": "Unique among the routes",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$",
            "title": "Name",
            "type": "string"
          },
          "timeout_ms": {
            "anyOf": [
              {
                "maximum": 300000.0,
                "minimum": 1000.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "How long a retriever waiting for the verdict waits at most (ms)",
            "title": "Timeout Ms"
          },
          "workflow": {
            "description": "The id of the workflow the email runs",
            "title": "Workflow",
            "type": "string"
          }
        },
        "required": [
          "name",
          "workflow"
        ],
        "title": "Route",
        "type": "object"
      },
      "RouteMatch": {
        "additionalProperties": false,
        "description": "What an email must be for a route to take it: every criterion given, any of its values.",
        "properties": {
          "recipient_groups": {
            "description": "Names of recipient groups: a recipient of the email is in one",
            "items": {
              "type": "string"
            },
            "title": "Recipient Groups",
            "type": "array"
          },
          "sender_domains": {
            "description": "\"example.com\", or \"*.example.com\" for its subdomains: the From domain, when a trusted DMARC result passed for it",
            "items": {
              "type": "string"
            },
            "title": "Sender Domains",
            "type": "array"
          },
          "sources": {
            "description": "Retriever ids, or \"api\" for the submissions",
            "items": {
              "type": "string"
            },
            "title": "Sources",
            "type": "array"
          }
        },
        "title": "RouteMatch",
        "type": "object"
      },
      "RoutingDocument": {
        "additionalProperties": false,
        "description": "Three sections of spot.yaml as they are written there; what PUT /api/v1/routing takes.",
        "properties": {
          "recipient_groups": {
            "description": "Each group's entries: addresses (local@domain) or domains (@domain)",
            "patternProperties": {
              "^[a-z0-9][a-z0-9_-]{0,63}$": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              }
            },
            "title": "Recipient Groups",
            "type": "object"
          },
          "routing": {
            "description": "The routes, in order: the first that matches wins",
            "items": {
              "$ref": "#/components/schemas/Route"
            },
            "title": "Routing",
            "type": "array"
          },
          "shadow": {
            "description": "The workflows run in the shadow, one entry each",
            "items": {
              "$ref": "#/components/schemas/ShadowEntry"
            },
            "title": "Shadow",
            "type": "array"
          }
        },
        "title": "RoutingDocument",
        "type": "object"
      },
      "RunMetrics": {
        "description": "A run's metrics: overall, per dataset and per analyzer, and against production's verdicts.",
        "properties": {
          "baseline": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Metrics"
              },
              {
                "type": "null"
              }
            ],
            "description": "Production's metrics on the same labelled emails, from each result's baseline_verdict: an email without one counts as failed, and the mean score is 0, as no score of production's is kept; None without production's verdicts"
          },
          "differences": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Emails whose class differs from production's; None without production's",
            "title": "Differences"
          },
          "latency": {
            "$ref": "#/components/schemas/Latency",
            "description": "The workflow's, over the emails it analysed"
          },
          "overall": {
            "$ref": "#/components/schemas/Metrics"
          },
          "per_analyzer": {
            "additionalProperties": {
              "$ref": "#/components/schemas/AnalyzerMetrics"
            },
            "title": "Per Analyzer",
            "type": "object"
          },
          "per_dataset": {
            "additionalProperties": {
              "$ref": "#/components/schemas/Metrics"
            },
            "title": "Per Dataset",
            "type": "object"
          },
          "workflow_versions": {
            "description": "The versions its results ran with",
            "items": {
              "type": "integer"
            },
            "title": "Workflow Versions",
            "type": "array"
          }
        },
        "required": [
          "overall",
          "latency",
          "per_dataset",
          "per_analyzer",
          "differences",
          "workflow_versions"
        ],
        "title": "RunMetrics",
        "type": "object"
      },
      "ScanResultResponse": {
        "description": "Response model for source scan operation.",
        "properties": {
          "message": {
            "title": "Message",
            "type": "string"
          },
          "new_plugins": {
            "items": {
              "type": "string"
            },
            "title": "New Plugins",
            "type": "array"
          },
          "plugins_found": {
            "title": "Plugins Found",
            "type": "integer"
          },
          "scan_duration_ms": {
            "title": "Scan Duration Ms",
            "type": "integer"
          },
          "source_id": {
            "title": "Source Id",
            "type": "string"
          },
          "updated_plugins": {
            "items": {
              "type": "string"
            },
            "title": "Updated Plugins",
            "type": "array"
          }
        },
        "required": [
          "source_id",
          "plugins_found",
          "new_plugins",
          "updated_plugins",
          "scan_duration_ms",
          "message"
        ],
        "title": "ScanResultResponse",
        "type": "object"
      },
      "SenderDomainIn": {
        "description": "The domain to list, and the email whose label it comes from.",
        "properties": {
          "domain": {
            "title": "Domain",
            "type": "string"
          },
          "email_id": {
            "anyOf": [
              {
                "format": "uuid",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email Id"
          }
        },
        "required": [
          "domain"
        ],
        "title": "SenderDomainIn",
        "type": "object"
      },
      "SenderLists": {
        "additionalProperties": false,
        "description": "The block and allow lists of sender domains.",
        "properties": {
          "allow": {
            "description": "The From domain, when a trusted DMARC result passed for it",
            "items": {
              "type": "string"
            },
            "title": "Allow",
            "type": "array"
          },
          "block": {
            "description": "The From domain, authenticated or not",
            "items": {
              "type": "string"
            },
            "title": "Block",
            "type": "array"
          }
        },
        "title": "SenderLists",
        "type": "object"
      },
      "SettingField": {
        "description": "One setting of a plugin, as ``spot.plugin.settings`` declares it.\n\nAn ``enum`` that no value can satisfy is refused, so that the plugin\nfails when its manifest is built rather than when an admin configures it:\nan empty one, or one whose values do not fit ``type`` (``integer``: int,\nnever bool; ``number``: int or float, never bool; ``boolean``: bool;\n``string``, ``url`` and ``secret``: str).\n\n``multiline`` marks a value of several lines, such as a PEM certificate:\nthe dashboard renders a textarea and keeps the line breaks. It is for\n``string`` and ``secret`` settings only; on any other type it is refused\nin the same way.\n\n``minimum`` and ``maximum`` bound an ``integer`` (by ints) or a ``number``\nsetting, and ``pattern`` a ``string`` setting, so that core refuses a\nvalue out of them when it is saved. Refused in the same way: a bound on\nanother type, a minimum above the maximum, a pattern that does not\ncompile, a default or an ``enum`` value out of them, and a ``number``'s\ndefault or ``enum`` value that is not finite (NaN, infinity).",
        "properties": {
          "default": {
            "title": "Default"
          },
          "description": {
            "default": "",
            "title": "Description",
            "type": "string"
          },
          "enum": {
            "anyOf": [
              {
                "items": {},
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Allowed values, each of the setting's type",
            "title": "Enum"
          },
          "maximum": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "The largest value allowed; integer and number only",
            "title": "Maximum"
          },
          "minimum": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "The smallest value allowed; integer and number only",
            "title": "Minimum"
          },
          "multiline": {
            "default": false,
            "description": "The value has several lines (a PEM certificate); string and secret only",
            "title": "Multiline",
            "type": "boolean"
          },
          "pattern": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "A regular expression the whole value matches, as Python's re.fullmatch reads it; string only. Core checks it when a value is saved; the dashboard shows it as a hint, as browsers read regular expressions otherwise",
            "title": "Pattern"
          },
          "required": {
            "default": false,
            "title": "Required",
            "type": "boolean"
          },
          "secret": {
            "default": false,
            "title": "Secret",
            "type": "boolean"
          },
          "type": {
            "$ref": "#/components/schemas/SettingType"
          }
        },
        "required": [
          "type"
        ],
        "title": "SettingField",
        "type": "object"
      },
      "SettingType": {
        "description": "Types of a plugin setting.",
        "enum": [
          "string",
          "integer",
          "number",
          "boolean",
          "url",
          "secret"
        ],
        "title": "SettingType",
        "type": "string"
      },
      "ShadowDifference": {
        "description": "A candidate workflow's verdict on an email, where it differs from production's.",
        "properties": {
          "at": {
            "description": "When the candidate decided",
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "candidate_decided_by": {
            "title": "Candidate Decided By",
            "type": "string"
          },
          "candidate_score": {
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Candidate Score",
            "type": "integer"
          },
          "candidate_verdict": {
            "$ref": "#/components/schemas/VerdictClass"
          },
          "candidate_workflow_id": {
            "description": "The shadow workflow",
            "title": "Candidate Workflow Id",
            "type": "string"
          },
          "candidate_workflow_version": {
            "title": "Candidate Workflow Version",
            "type": "integer"
          },
          "email_id": {
            "format": "uuid",
            "title": "Email Id",
            "type": "string"
          },
          "job_id": {
            "description": "The production job",
            "title": "Job Id",
            "type": "string"
          },
          "score": {
            "description": "Production's score",
            "maximum": 100.0,
            "minimum": 0.0,
            "title": "Score",
            "type": "integer"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass",
            "description": "Production's verdict"
          },
          "workflow_id": {
            "description": "The production workflow",
            "title": "Workflow Id",
            "type": "string"
          }
        },
        "required": [
          "job_id",
          "email_id",
          "workflow_id",
          "verdict",
          "score",
          "candidate_workflow_id",
          "candidate_workflow_version",
          "candidate_verdict",
          "candidate_score",
          "candidate_decided_by",
          "at"
        ],
        "title": "ShadowDifference",
        "type": "object"
      },
      "ShadowDifferencePage": {
        "description": "A workflow's shadow differences, newest first: verdicts and scores, never a result.",
        "properties": {
          "compared": {
            "description": "The production results the workflow ran on in those days; an email analysed again counts again",
            "title": "Compared",
            "type": "integer"
          },
          "days": {
            "description": "The UTC days the rate covers, today's included",
            "title": "Days",
            "type": "integer"
          },
          "differed": {
            "description": "Those on which its verdict class differed",
            "title": "Differed",
            "type": "integer"
          },
          "differences": {
            "items": {
              "$ref": "#/components/schemas/ShadowDifference"
            },
            "title": "Differences",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "differences",
          "total",
          "limit",
          "offset",
          "days",
          "compared",
          "differed"
        ],
        "title": "ShadowDifferencePage",
        "type": "object"
      },
      "ShadowEntry": {
        "additionalProperties": false,
        "description": "A workflow run besides production on the emails a match holds for, to compare.",
        "properties": {
          "match": {
            "$ref": "#/components/schemas/RouteMatch",
            "description": "Empty: every email"
          },
          "workflow": {
            "description": "The id of the workflow run in the shadow",
            "title": "Workflow",
            "type": "string"
          }
        },
        "required": [
          "workflow"
        ],
        "title": "ShadowEntry",
        "type": "object"
      },
      "Signal": {
        "description": "One piece of evidence behind a verdict.",
        "properties": {
          "evidence": {
            "default": "",
            "description": "Short human-readable proof",
            "title": "Evidence",
            "type": "string"
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "\"header:From\", \"url:3\", \"part:1.2\"",
            "title": "Location"
          },
          "name": {
            "description": "e.g. auth.dmarc_fail",
            "pattern": "^[a-z0-9_]+(\\.[a-z0-9_]+)+$",
            "title": "Name",
            "type": "string"
          },
          "weight": {
            "description": "Contribution, in score points",
            "maximum": 100.0,
            "minimum": -100.0,
            "title": "Weight",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "weight"
        ],
        "title": "Signal",
        "type": "object"
      },
      "SourceCreateRequest": {
        "description": "Request model for creating a plugin source.",
        "properties": {
          "auth_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Auth token for the Git forge / container registry; none for an index. Write-only: encrypted at rest and never returned in responses. Leave unset to use anonymous access. Supply over HTTPS only — the plaintext must never transit an untrusted network.",
            "title": "Auth Token"
          },
          "enabled": {
            "default": true,
            "description": "Whether source is active",
            "title": "Enabled",
            "type": "boolean"
          },
          "id": {
            "description": "Unique identifier",
            "maxLength": 50,
            "minLength": 1,
            "title": "Id",
            "type": "string"
          },
          "image_namespace": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Container-registry namespace for the plugin images, when it differs from `organization`. For GitLab installs the Git group and the registry namespace are the same string and this can stay None. For Codeberg + Docker Hub they diverge (e.g. Git org `SPOT_Project`, Docker Hub user `spotproject`); set this to the Docker Hub username.",
            "title": "Image Namespace"
          },
          "name": {
            "description": "Display name",
            "maxLength": 100,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          },
          "organization": {
            "default": "",
            "description": "Organisation/group path containing plugins; none for an index",
            "title": "Organization",
            "type": "string"
          },
          "priority": {
            "default": 100,
            "description": "Source priority (lower = higher priority)",
            "maximum": 1000.0,
            "minimum": 1.0,
            "title": "Priority",
            "type": "integer"
          },
          "registry": {
            "default": "",
            "description": "Container registry URL (host only, e.g. registry-1.docker.io or gitlab.example.com:5050); none for an index",
            "title": "Registry",
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/SourceType",
            "description": "Source type"
          },
          "url": {
            "description": "Git forge or registry base URL; an index's index.json URL",
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "name",
          "type",
          "url",
          "id"
        ],
        "title": "SourceCreateRequest",
        "type": "object"
      },
      "SourceListResponse": {
        "description": "Response model for list of sources.",
        "properties": {
          "sources": {
            "items": {
              "$ref": "#/components/schemas/SourceResponse"
            },
            "title": "Sources",
            "type": "array"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          }
        },
        "required": [
          "sources",
          "total"
        ],
        "title": "SourceListResponse",
        "type": "object"
      },
      "SourceResponse": {
        "description": "Response model for a plugin source.",
        "properties": {
          "enabled": {
            "title": "Enabled",
            "type": "boolean"
          },
          "has_auth_token": {
            "default": false,
            "title": "Has Auth Token",
            "type": "boolean"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "image_namespace": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Image Namespace"
          },
          "index_published": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the signed index the last scan read was published; null otherwise",
            "title": "Index Published"
          },
          "index_serial": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The serial of the signed index the last scan read; null for a source of another type, or one not scanned yet",
            "title": "Index Serial"
          },
          "last_scan": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Scan"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "organization": {
            "title": "Organization",
            "type": "string"
          },
          "plugin_count": {
            "title": "Plugin Count",
            "type": "integer"
          },
          "priority": {
            "title": "Priority",
            "type": "integer"
          },
          "registry": {
            "title": "Registry",
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/SourceType"
          },
          "url": {
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "url",
          "organization",
          "registry",
          "enabled",
          "priority",
          "last_scan",
          "plugin_count"
        ],
        "title": "SourceResponse",
        "type": "object"
      },
      "SourceType": {
        "description": "Supported plugin source types.\n\nTwo families share the same configuration shape:\n\n- **Forge-driven** (``gitlab``, ``gitea``): enumerate via the Git\n  forge API, fetch OCI labels via the configured registry. Gives\n  free Git source links.\n- **Registry-driven** (``dockerhub``, ``ghcr``, ``gitlab_registry``,\n  ``gitea_registry``): enumerate directly from the container\n  registry; the Git forge is not consulted. Source links are\n  recovered from the ``org.opencontainers.image.source`` OCI label\n  when the plugin Dockerfile sets it.\n\nAn index (``index``) is a signed file listing plugins and their images\nby digest (``plugin_index``).",
        "enum": [
          "gitlab",
          "gitea",
          "dockerhub",
          "ghcr",
          "gitlab_registry",
          "gitea_registry",
          "index"
        ],
        "title": "SourceType",
        "type": "string"
      },
      "SourceUpdateRequest": {
        "description": "Request model for updating a plugin source.",
        "properties": {
          "auth_token": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Auth Token"
          },
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Enabled"
          },
          "image_namespace": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Image Namespace"
          },
          "name": {
            "anyOf": [
              {
                "maxLength": 100,
                "minLength": 1,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "organization": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Organization"
          },
          "priority": {
            "anyOf": [
              {
                "maximum": 1000.0,
                "minimum": 1.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Priority"
          },
          "registry": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Registry"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Url"
          }
        },
        "title": "SourceUpdateRequest",
        "type": "object"
      },
      "SystemUpdateResponse": {
        "description": "Update status returned to the dashboard.",
        "properties": {
          "available_version": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Newer version tag available on Codeberg, or null if up to date",
            "title": "Available Version"
          },
          "checked_at": {
            "description": "Timestamp of the underlying check",
            "format": "date-time",
            "title": "Checked At",
            "type": "string"
          },
          "compose_modified": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "True when the local docker-compose.yml diverges from upstream. Null when the check can't run (not an install from the deploy/ directory of the spot repository, or no network).",
            "title": "Compose Modified"
          },
          "current_version": {
            "description": "Running release, or 'unknown'",
            "title": "Current Version",
            "type": "string"
          },
          "deploy_managed": {
            "default": false,
            "description": "True when the bind-mount of the docker-compose.yml of deploy/ is present, i.e. SPOT was installed from the deploy/ directory of the spot repository. The dashboard branches the upgrade command on this flag.",
            "title": "Deploy Managed",
            "type": "boolean"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Reason the last check failed; null on success",
            "title": "Error"
          },
          "release_notes_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Codeberg page of the available version's tag",
            "title": "Release Notes Url"
          },
          "released_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the available version was published",
            "title": "Released At"
          }
        },
        "required": [
          "current_version",
          "checked_at"
        ],
        "title": "SystemUpdateResponse",
        "type": "object"
      },
      "Tier": {
        "description": "A plugin's tier in the signed index (spot/spot#41).",
        "enum": [
          "official",
          "verified",
          "community"
        ],
        "title": "Tier",
        "type": "string"
      },
      "Token": {
        "description": "OAuth2 token response model.",
        "properties": {
          "access_token": {
            "description": "JWT access token",
            "title": "Access Token",
            "type": "string"
          },
          "expires_in": {
            "description": "Token expiration time in seconds",
            "title": "Expires In",
            "type": "integer"
          },
          "refresh_token": {
            "description": "JWT refresh token",
            "title": "Refresh Token",
            "type": "string"
          },
          "token_type": {
            "default": "bearer",
            "description": "Token type (always 'bearer')",
            "title": "Token Type",
            "type": "string"
          }
        },
        "required": [
          "access_token",
          "refresh_token",
          "expires_in"
        ],
        "title": "Token",
        "type": "object"
      },
      "Url": {
        "description": "A URL found in the message.",
        "properties": {
          "anchor_text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "For html_href: the visible text of the link",
            "title": "Anchor Text"
          },
          "found_in": {
            "description": "html_text: written in the HTML's visible text, not in an attribute",
            "enum": [
              "text",
              "html_href",
              "html_text",
              "header"
            ],
            "title": "Found In",
            "type": "string"
          },
          "host": {
            "description": "The host a browser would reach, as the WHATWG URL Standard reads it: percent-decoded, UTS 46-mapped (lower-cased...), A-labels decoded when UTS 46 leaves them as they are, no trailing dots; one browsers refuse is kept as read, one that can be no DNS name only lower-cased: over 1 012 characters, a label over 252 or over 63 combining marks in a row as UTS 46 maps them, invisible ones aside (DNS allows 253 and 63, NFC composes at most 4 code points into one, and an A-label spells each mark with a character at least); one whose A-labels not yet decoded do not fit in the 32 768 characters of distinct A-labels a message decodes keeps all its A-labels encoded, as UTS 46 maps them (host_limit); \"\" when none",
            "title": "Host",
            "type": "string"
          },
          "part_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Part Id"
          },
          "url": {
            "description": "As found",
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "url",
          "host",
          "found_in",
          "part_id",
          "anchor_text"
        ],
        "title": "Url",
        "type": "object"
      },
      "UserCreate": {
        "description": "User registration request model (admin-only).",
        "properties": {
          "email": {
            "description": "User email address",
            "format": "email",
            "title": "Email",
            "type": "string"
          },
          "password": {
            "description": "User password",
            "minLength": 8,
            "title": "Password",
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/UserRole",
            "default": "analyst",
            "description": "User role"
          },
          "username": {
            "description": "Username",
            "maxLength": 50,
            "minLength": 3,
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "username",
          "email",
          "password"
        ],
        "title": "UserCreate",
        "type": "object"
      },
      "UserCreateRequest": {
        "description": "Request model for creating a user.",
        "properties": {
          "email": {
            "format": "email",
            "title": "Email",
            "type": "string"
          },
          "password": {
            "minLength": 8,
            "title": "Password",
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/UserRole",
            "default": "analyst"
          },
          "username": {
            "maxLength": 50,
            "minLength": 3,
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "username",
          "email",
          "password"
        ],
        "title": "UserCreateRequest",
        "type": "object"
      },
      "UserListResponse": {
        "description": "Response model for paginated user list.",
        "properties": {
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          },
          "users": {
            "items": {
              "$ref": "#/components/schemas/UserResponse"
            },
            "title": "Users",
            "type": "array"
          }
        },
        "required": [
          "users",
          "total",
          "limit",
          "offset"
        ],
        "title": "UserListResponse",
        "type": "object"
      },
      "UserPasswordUpdateRequest": {
        "description": "Request model for changing user password.",
        "properties": {
          "current_password": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Required when user changes own password",
            "title": "Current Password"
          },
          "new_password": {
            "minLength": 8,
            "title": "New Password",
            "type": "string"
          }
        },
        "required": [
          "new_password"
        ],
        "title": "UserPasswordUpdateRequest",
        "type": "object"
      },
      "UserProfile": {
        "description": "User profile response model.",
        "properties": {
          "email": {
            "description": "User email",
            "title": "Email",
            "type": "string"
          },
          "id": {
            "description": "User UUID",
            "title": "Id",
            "type": "string"
          },
          "is_active": {
            "description": "Whether account is active",
            "title": "Is Active",
            "type": "boolean"
          },
          "role": {
            "description": "User role (admin, analyst, viewer)",
            "title": "Role",
            "type": "string"
          },
          "username": {
            "description": "Username",
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "id",
          "username",
          "email",
          "is_active",
          "role"
        ],
        "title": "UserProfile",
        "type": "object"
      },
      "UserResponse": {
        "description": "Response model for user data.",
        "properties": {
          "created_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created At"
          },
          "email": {
            "title": "Email",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "is_active": {
            "title": "Is Active",
            "type": "boolean"
          },
          "role": {
            "title": "Role",
            "type": "string"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Updated At"
          },
          "username": {
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "id",
          "username",
          "email",
          "role",
          "is_active"
        ],
        "title": "UserResponse",
        "type": "object"
      },
      "UserRole": {
        "description": "User roles for SPOT platform access control.",
        "enum": [
          "admin",
          "analyst",
          "viewer"
        ],
        "title": "UserRole",
        "type": "string"
      },
      "UserRoleUpdateRequest": {
        "description": "Request model for changing user role.",
        "properties": {
          "role": {
            "$ref": "#/components/schemas/UserRole"
          }
        },
        "required": [
          "role"
        ],
        "title": "UserRoleUpdateRequest",
        "type": "object"
      },
      "UserUpdateRequest": {
        "description": "Request model for updating a user.",
        "properties": {
          "email": {
            "anyOf": [
              {
                "format": "email",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email"
          },
          "username": {
            "anyOf": [
              {
                "maxLength": 50,
                "minLength": 3,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Username"
          }
        },
        "title": "UserUpdateRequest",
        "type": "object"
      },
      "ValidateRequest": {
        "description": "Body of POST /api/v1/config/validate.",
        "properties": {
          "content": {
            "description": "Raw YAML text to validate",
            "title": "Content",
            "type": "string"
          },
          "kind": {
            "const": "spot",
            "default": "spot",
            "description": "Schema to validate against: spot.yaml's, the only one",
            "title": "Kind",
            "type": "string"
          }
        },
        "required": [
          "content"
        ],
        "title": "ValidateRequest",
        "type": "object"
      },
      "ValidateResponse": {
        "description": "Result of POST /api/v1/config/validate.",
        "properties": {
          "errors": {
            "items": {
              "$ref": "#/components/schemas/ValidationIssue"
            },
            "title": "Errors",
            "type": "array"
          },
          "valid": {
            "title": "Valid",
            "type": "boolean"
          }
        },
        "required": [
          "valid"
        ],
        "title": "ValidateResponse",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      },
      "ValidationIssue": {
        "description": "One validation error, with a JSON-pointer-style path.",
        "properties": {
          "message": {
            "description": "Human-readable error message",
            "title": "Message",
            "type": "string"
          },
          "path": {
            "description": "Dotted path to the offending field",
            "title": "Path",
            "type": "string"
          }
        },
        "required": [
          "path",
          "message"
        ],
        "title": "ValidationIssue",
        "type": "object"
      },
      "VerdictClass": {
        "description": "The class of an email, for one analyzer or for the workflow.",
        "enum": [
          "malicious",
          "suspicious",
          "spam",
          "benign",
          "unknown"
        ],
        "title": "VerdictClass",
        "type": "string"
      },
      "VerdictPolicy": {
        "description": "How a workflow turns its analyzers' answers into the email's verdict.",
        "properties": {
          "decision_stages": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "minItems": 1,
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "The stages whose analyzers count; None: every stage",
            "title": "Decision Stages"
          },
          "decisive_signals": {
            "items": {
              "$ref": "#/components/schemas/DecisiveSignal"
            },
            "title": "Decisive Signals",
            "type": "array"
          },
          "malicious_threshold": {
            "default": 80,
            "maximum": 100.0,
            "minimum": 1.0,
            "title": "Malicious Threshold",
            "type": "integer"
          },
          "method": {
            "$ref": "#/components/schemas/AggregationMethod",
            "default": "weighted_mean"
          },
          "suspicious_threshold": {
            "default": 50,
            "maximum": 100.0,
            "minimum": 1.0,
            "title": "Suspicious Threshold",
            "type": "integer"
          }
        },
        "title": "VerdictPolicy",
        "type": "object"
      },
      "Workflow": {
        "description": "Complete workflow configuration for email analysis.",
        "properties": {
          "created_by": {
            "default": "system",
            "description": "Creator of the workflow",
            "title": "Created By",
            "type": "string"
          },
          "description": {
            "default": "",
            "description": "Workflow description",
            "title": "Description",
            "type": "string"
          },
          "id": {
            "description": "Workflow ID: a letter or digit, then up to 127 letters, digits, dots, underscores or hyphens",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$",
            "title": "Id",
            "type": "string"
          },
          "name": {
            "description": "Workflow name",
            "title": "Name",
            "type": "string"
          },
          "on_verdict": {
            "additionalProperties": {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            "description": "Responder ids to call, by verdict",
            "propertyNames": {
              "$ref": "#/components/schemas/VerdictClass"
            },
            "title": "On Verdict",
            "type": "object"
          },
          "stages": {
            "description": "List of workflow stages",
            "items": {
              "$ref": "#/components/schemas/WorkflowStage"
            },
            "title": "Stages",
            "type": "array"
          },
          "timeout_ms": {
            "default": 300000,
            "description": "Total workflow timeout in milliseconds",
            "maximum": 600000.0,
            "minimum": 1000.0,
            "title": "Timeout Ms",
            "type": "integer"
          },
          "verdict_policy": {
            "$ref": "#/components/schemas/VerdictPolicy"
          },
          "version": {
            "default": 1,
            "description": "Workflow version",
            "minimum": 1.0,
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "title": "Workflow",
        "type": "object"
      },
      "WorkflowAtCommit": {
        "description": "A workflow as a commit stores it, and what that commit changed in it.",
        "properties": {
          "commit": {
            "$ref": "#/components/schemas/CommitSummary"
          },
          "diff": {
            "description": "A unified diff of the workflow from the commit before it",
            "title": "Diff",
            "type": "string"
          },
          "workflow": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "The workflow as the commit stores it; null when it holds none",
            "title": "Workflow"
          }
        },
        "required": [
          "commit",
          "workflow",
          "diff"
        ],
        "title": "WorkflowAtCommit",
        "type": "object"
      },
      "WorkflowCloneRequest": {
        "description": "The body of a clone: the new workflow's id, and its name.",
        "properties": {
          "id": {
            "description": "The new workflow's id: letters, digits, '-' and '_', at most 100",
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9_-]+$",
            "title": "Id",
            "type": "string"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Its name; the source's when not given",
            "title": "Name"
          }
        },
        "required": [
          "id"
        ],
        "title": "WorkflowCloneRequest",
        "type": "object"
      },
      "WorkflowHistoryPage": {
        "description": "A page of a workflow's history, newest first.",
        "properties": {
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "description": "The commits that changed the workflow",
            "title": "Total",
            "type": "integer"
          },
          "versions": {
            "items": {
              "$ref": "#/components/schemas/WorkflowVersion"
            },
            "title": "Versions",
            "type": "array"
          }
        },
        "required": [
          "versions",
          "total",
          "limit",
          "offset"
        ],
        "title": "WorkflowHistoryPage",
        "type": "object"
      },
      "WorkflowListResponse": {
        "description": "Paginated workflow list response.",
        "properties": {
          "default_workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Default Workflow Id"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "total": {
            "title": "Total",
            "type": "integer"
          },
          "workflows": {
            "items": {
              "$ref": "#/components/schemas/WorkflowSummary"
            },
            "title": "Workflows",
            "type": "array"
          }
        },
        "required": [
          "workflows",
          "total",
          "limit",
          "offset"
        ],
        "title": "WorkflowListResponse",
        "type": "object"
      },
      "WorkflowStage": {
        "description": "A stage in a workflow with multiple analyzers.",
        "properties": {
          "analyzers": {
            "description": "List of analyzers in this stage",
            "items": {
              "$ref": "#/components/schemas/AnalyzerConfig"
            },
            "title": "Analyzers",
            "type": "array"
          },
          "condition": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Conditional execution expression",
            "title": "Condition"
          },
          "depends_on": {
            "description": "Names of stages this depends on",
            "items": {
              "type": "string"
            },
            "title": "Depends On",
            "type": "array"
          },
          "name": {
            "description": "Stage name",
            "title": "Name",
            "type": "string"
          },
          "retrieval_limits": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RetrievalLimits"
              },
              {
                "type": "null"
              }
            ],
            "description": "Operator-policy caps on knowledge-store retrieval for this stage"
          },
          "type": {
            "default": "sequential",
            "description": "Stage type (parallel, sequential, conditional)",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkflowStage",
        "type": "object"
      },
      "WorkflowSummary": {
        "description": "A workflow in the list: its identity and the number of its stages.",
        "properties": {
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Id"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "stages": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stages"
          },
          "version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Version"
          }
        },
        "title": "WorkflowSummary",
        "type": "object"
      },
      "WorkflowVersion": {
        "description": "A commit that changed a workflow, and the workflow's version in it.",
        "properties": {
          "commit": {
            "$ref": "#/components/schemas/CommitSummary"
          },
          "deleted": {
            "description": "Whether the commit deleted it",
            "title": "Deleted",
            "type": "boolean"
          },
          "version": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Its version in that commit; null when the commit deleted it, or the version it stores is not an integer",
            "title": "Version"
          }
        },
        "required": [
          "commit",
          "version",
          "deleted"
        ],
        "title": "WorkflowVersion",
        "type": "object"
      },
      "WorkflowsConfigResponse": {
        "properties": {
          "config_version": {
            "title": "Config Version",
            "type": "string"
          },
          "workflows": {
            "items": {
              "$ref": "#/components/schemas/Workflow"
            },
            "title": "Workflows",
            "type": "array"
          }
        },
        "required": [
          "workflows",
          "config_version"
        ],
        "title": "WorkflowsConfigResponse",
        "type": "object"
      }
    },
    "securitySchemes": {
      "HTTPBearer": {
        "scheme": "bearer",
        "type": "http"
      },
      "MetricsToken": {
        "description": "The metrics token (METRICS_TOKEN)",
        "scheme": "bearer",
        "type": "http"
      },
      "PluginToken": {
        "description": "The plugin's own token, spot_pt_...",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "SPOT Anti-Phishing Platform API",
    "title": "SPOT API Gateway",
    "version": "2.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/analyze": {
      "get": {
        "description": "List recent analysis jobs with optional filtering and pagination.\n\nIn-flight jobs (``queued`` / ``processing``) live in Redis until the\norchestrator persists their verdict to the DB; we surface them at the\nhead of the list so a freshly-submitted analysis is visible to the\noperator while it is being processed, instead of appearing only at\ncompletion. They have no verdict yet, so a verdict filter leaves them out.\n\nEach job carries its verdict, score and ``decided_by``, and an ``email``\nsummary (subject and sender) so list views can name the email without\nfetching it. A parameter other than the filters below is refused (422).",
        "operationId": "list_analysis_jobs_api_v1_analyze_get",
        "parameters": [
          {
            "description": "Filter by job status",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by job status",
              "title": "Status"
            }
          },
          {
            "description": "Filter by email ID",
            "in": "query",
            "name": "email_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by email ID",
              "title": "Email Id"
            }
          },
          {
            "description": "Filter by workflow ID",
            "in": "query",
            "name": "workflow_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by workflow ID",
              "title": "Workflow Id"
            }
          },
          {
            "description": "Filter by verdict",
            "in": "query",
            "name": "verdict",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VerdictClass"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by verdict",
              "title": "Verdict"
            }
          },
          {
            "description": "Maximum items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum items to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of items to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Analysis Jobs",
        "tags": [
          "Email Analysis"
        ]
      },
      "head": {
        "description": "List recent analysis jobs with optional filtering and pagination.\n\nIn-flight jobs (``queued`` / ``processing``) live in Redis until the\norchestrator persists their verdict to the DB; we surface them at the\nhead of the list so a freshly-submitted analysis is visible to the\noperator while it is being processed, instead of appearing only at\ncompletion. They have no verdict yet, so a verdict filter leaves them out.\n\nEach job carries its verdict, score and ``decided_by``, and an ``email``\nsummary (subject and sender) so list views can name the email without\nfetching it. A parameter other than the filters below is refused (422).",
        "operationId": "list_analysis_jobs_api_v1_analyze_head",
        "parameters": [
          {
            "description": "Filter by job status",
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by job status",
              "title": "Status"
            }
          },
          {
            "description": "Filter by email ID",
            "in": "query",
            "name": "email_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by email ID",
              "title": "Email Id"
            }
          },
          {
            "description": "Filter by workflow ID",
            "in": "query",
            "name": "workflow_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by workflow ID",
              "title": "Workflow Id"
            }
          },
          {
            "description": "Filter by verdict",
            "in": "query",
            "name": "verdict",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/VerdictClass"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by verdict",
              "title": "Verdict"
            }
          },
          {
            "description": "Maximum items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum items to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of items to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Analysis Jobs",
        "tags": [
          "Email Analysis"
        ]
      },
      "post": {
        "description": "Submit an email for phishing analysis.\n\nThe body is the message itself (``message/rfc822``, at most 25 MiB), or\nJSON with the message in base64 and an optional workflow; without one,\nthe email's route chooses it, or the default workflow runs. The job is in\nobserve: a submission comes through no mail connection. The gateway\nparses and stores it, then queues the analysis and returns at once with a\njob ID to poll.\n\nA message larger than 25 MiB is refused (413), and so is an empty one or\none that cannot be parsed within the limits (422). Nothing is stored then.\nWhen the gateway already takes as many messages as it can, the answer is\n503, to retry later.\n\n**Workflow:**\n1. Submit email via this endpoint\n2. Receive job_id in response\n3. Poll GET /api/v1/analyze/{job_id} for status and results",
        "operationId": "analyze_email_api_v1_analyze_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalysisSubmission"
              }
            },
            "message/rfc822": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Analyze Email",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/analyze/actions": {
      "get": {
        "description": "What was done with the emails of the last ``days`` × 24 hours, or would have been in observe.\n\nThe retrievers' own actions and the responders called, counted by actor,\nkind (``inline`` or ``responder``), action, mode and whether each was\napplied: in observe, nothing is. ``connections`` gives every mail\nconnection's current mode, those without actions in the window too.",
        "operationId": "get_action_summary_api_v1_analyze_actions_get",
        "parameters": [
          {
            "description": "The window: the last days × 24 hours, up to now",
            "in": "query",
            "name": "days",
            "required": false,
            "schema": {
              "default": 7,
              "description": "The window: the last days × 24 hours, up to now",
              "maximum": 90,
              "minimum": 1,
              "title": "Days",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionSummaryResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Action Summary",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/analyze/stats": {
      "get": {
        "description": "Get aggregated analysis statistics.\n\nCounts analyses in total, by status and by verdict (every class present;\na failed analysis counts as unknown), computed server-side via direct DB\nqueries.",
        "operationId": "get_analysis_stats_api_v1_analyze_stats_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisStatsResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Analysis Stats",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/analyze/stats/daily": {
      "get": {
        "description": "Get per-day analysis statistics for the last ``days`` UTC days.\n\nOne entry per day, oldest first, including days without analyses:\ntotal and failed counts, analyses by verdict (every class present; a\nfailed analysis counts as unknown), and the nearest-rank median and p95\nexecution time of completed analyses.",
        "operationId": "get_daily_analysis_stats_api_v1_analyze_stats_daily_get",
        "parameters": [
          {
            "description": "Number of UTC days, today included",
            "in": "query",
            "name": "days",
            "required": false,
            "schema": {
              "default": 30,
              "description": "Number of UTC days, today included",
              "maximum": 90,
              "minimum": 1,
              "title": "Days",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DailyAnalysisStatsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Daily Analysis Stats",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/analyze/{job_id}": {
      "delete": {
        "description": "Cancel a job queued or running.\n\nA running job's analysis stops at once; a queued one never starts. The job\nis recorded ``cancelled``, with an ``unknown`` verdict decided by\n``cancelled``, and no responder is called. Cancelling a cancelled job\nanswers the same; a completed or failed one is 409.",
        "operationId": "cancel_analysis_job_api_v1_analyze__job_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No job has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The job is finished: completed or failed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator answers 500, or the call to it fails otherwise; the message says what"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator refused the gateway's internal key"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Retry the cancel. The database or the analyzer-orchestrator is not available, or its job processor is not running: nothing was cancelled. Or the orchestrator could not record the cancel: it keeps it pending, and does not run the job meanwhile unless it restarts"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator does not answer in time"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Cancel Analysis Job",
        "tags": [
          "Email Analysis"
        ]
      },
      "get": {
        "description": "Get the current status and results of an analysis job.\n\nPoll this endpoint to check the progress of an email analysis job.\nOnce the job is finished the response carries the workflow's verdict,\nscore and ``decided_by``, the signals behind the verdict and the\nworkflow's result, and the email's summary (subject, sender, ...). The\nemail's content is for analysts and admins, at\n``/api/v1/emails/{email_id}``; so is the text analyzers quote from it:\nfor a viewer, the signals' evidence and location and the analyzers'\nsummaries and details are left empty. Its actions say what was done with\nthe email, or would have been in observe. Its label is the analyst's verdict\non the email, with a note that viewers read empty.\n\n**Job Status Values:**\n- queued: Job is waiting to be processed\n- processing: Analysis is currently running\n- completed: Analysis finished; its verdict may still be unknown\n- failed: Analysis encountered an error; the verdict is unknown",
        "operationId": "get_analysis_status_api_v1_analyze__job_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailAnalysisStatusWithEmail"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Analysis Status",
        "tags": [
          "Email Analysis"
        ]
      },
      "head": {
        "description": "Get the current status and results of an analysis job.\n\nPoll this endpoint to check the progress of an email analysis job.\nOnce the job is finished the response carries the workflow's verdict,\nscore and ``decided_by``, the signals behind the verdict and the\nworkflow's result, and the email's summary (subject, sender, ...). The\nemail's content is for analysts and admins, at\n``/api/v1/emails/{email_id}``; so is the text analyzers quote from it:\nfor a viewer, the signals' evidence and location and the analyzers'\nsummaries and details are left empty. Its actions say what was done with\nthe email, or would have been in observe. Its label is the analyst's verdict\non the email, with a note that viewers read empty.\n\n**Job Status Values:**\n- queued: Job is waiting to be processed\n- processing: Analysis is currently running\n- completed: Analysis finished; its verdict may still be unknown\n- failed: Analysis encountered an error; the verdict is unknown",
        "operationId": "get_analysis_status_api_v1_analyze__job_id__head",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailAnalysisStatusWithEmail"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Analysis Status",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/audit": {
      "get": {
        "description": "The audit log, newest first, with the total of the entries that pass the filters (admins).",
        "operationId": "list_audit_api_v1_audit_get",
        "parameters": [
          {
            "description": "Maximum entries to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum entries to return",
              "maximum": 500,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of entries to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of entries to skip",
              "maximum": 9223372036854775807,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          },
          {
            "description": "An action, `plugin.install`, or a family ending in `.`: `plugin.`",
            "in": "query",
            "name": "action",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "pattern": "^[a-z_]+(\\.[a-z_]*)?$",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "An action, `plugin.install`, or a family ending in `.`: `plugin.`",
              "title": "Action"
            }
          },
          {
            "description": "Who acted, as named then: a username or a client id",
            "in": "query",
            "name": "actor",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 255,
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Who acted, as named then: a username or a client id",
              "title": "Actor"
            }
          },
          {
            "description": "Who acted, by an id a rename keeps: a user's UUID or a client id",
            "in": "query",
            "name": "actor_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 255,
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Who acted, by an id a rename keeps: a user's UUID or a client id",
              "title": "Actor Id"
            }
          },
          {
            "description": "What was acted on: a plugin, a username, a source, an email, a document or a queue",
            "in": "query",
            "name": "target",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 255,
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "What was acted on: a plugin, a username, a source, an email, a document or a queue",
              "title": "Target"
            }
          },
          {
            "description": "`success` or `failure`",
            "in": "query",
            "name": "outcome",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "success",
                    "failure"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "`success` or `failure`",
              "title": "Outcome"
            }
          },
          {
            "description": "From this time, included (ISO 8601; UTC without an offset)",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "format": "date-time",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "From this time, included (ISO 8601; UTC without an offset)",
              "title": "Since"
            }
          },
          {
            "description": "Up to this time, included (ISO 8601; UTC without an offset)",
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "format": "date-time",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Up to this time, included (ISO 8601; UTC without an offset)",
              "title": "Until"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Audit",
        "tags": [
          "Audit"
        ]
      }
    },
    "/api/v1/benchmarks": {
      "get": {
        "description": "The runs, newest first. Viewers read them: they hold no content.",
        "operationId": "list_benchmarks_api_v1_benchmarks_get",
        "parameters": [
          {
            "description": "Only the runs of this workflow",
            "in": "query",
            "name": "workflow_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only the runs of this workflow",
              "title": "Workflow Id"
            }
          },
          {
            "description": "Maximum runs to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum runs to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of runs to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of runs to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkRunPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Benchmarks",
        "tags": [
          "Benchmarks"
        ]
      },
      "post": {
        "description": "Start a run of a workflow.\n\nA run over spot-cli's files (``files``) is spot-cli's: it submits the\nemails, reports their results and finishes the run. A run over a\n``dataset`` or over the stored ``mail`` of a period is the platform's:\none message per email is queued, apart from production's, and the\nrun's ``selection_sha256`` fingerprints the emails and their labels. A\nperiod of stored mail without an ``until`` ends now, which the run\nrecords, so that its emails can be drawn again. One platform run runs at\na time: another is refused with 409 while one runs.\n\nThe run's snapshot is taken now: the workflow's version, spot.yaml's last\ncommit, SPOT's version, and each analyzer's version and image.",
        "operationId": "start_benchmark_api_v1_benchmarks_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BenchmarkStart"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow that loads has this id; or no dataset has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A platform run is running"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, no stored email matches, or the dataset holds too many"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The database is not available, or the message broker is unavailable for a platform run"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Start Benchmark",
        "tags": [
          "Benchmarks"
        ]
      }
    },
    "/api/v1/benchmarks/{run_id}": {
      "delete": {
        "description": "Delete a run and its results.",
        "operationId": "delete_benchmark_api_v1_benchmarks__run_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Benchmark",
        "tags": [
          "Benchmarks"
        ]
      },
      "get": {
        "description": "A run, with its snapshot, the emails recorded so far and, once ended, its metrics.",
        "operationId": "get_benchmark_api_v1_benchmarks__run_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Benchmark",
        "tags": [
          "Benchmarks"
        ]
      }
    },
    "/api/v1/benchmarks/{run_id}/cancel": {
      "post": {
        "description": "Cancel a running run, its metrics counted over the results recorded so far.\n\nA platform run's messages still queued are skipped; a run spot-cli runs\nrefuses its next batch, and spot-cli stops, saying why.",
        "operationId": "cancel_benchmark_api_v1_benchmarks__run_id__cancel_post",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The run has ended"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Cancel Benchmark",
        "tags": [
          "Benchmarks"
        ]
      }
    },
    "/api/v1/benchmarks/{run_id}/finish": {
      "post": {
        "description": "End a run spot-cli runs, its metrics counted over the results recorded.\n\nA run that has already ended is answered as it is.",
        "operationId": "finish_benchmark_api_v1_benchmarks__run_id__finish_post",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The platform runs this run"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Finish Benchmark",
        "tags": [
          "Benchmarks"
        ]
      }
    },
    "/api/v1/benchmarks/{run_id}/results": {
      "get": {
        "description": "A run's results in the order recorded. Viewers read them: they hold no content.",
        "operationId": "list_benchmark_results_api_v1_benchmarks__run_id__results_get",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          },
          {
            "description": "Only the emails the workflow got wrong",
            "in": "query",
            "name": "missed",
            "required": false,
            "schema": {
              "default": false,
              "description": "Only the emails the workflow got wrong",
              "title": "Missed",
              "type": "boolean"
            }
          },
          {
            "description": "Only the emails production decided otherwise",
            "in": "query",
            "name": "differs",
            "required": false,
            "schema": {
              "default": false,
              "description": "Only the emails production decided otherwise",
              "title": "Differs",
              "type": "boolean"
            }
          },
          {
            "description": "Maximum results to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum results to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of results to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of results to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BenchmarkResultPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Benchmark Results",
        "tags": [
          "Benchmarks"
        ]
      },
      "post": {
        "description": "Record results of a run spot-cli runs, each in place of the one of the same key.",
        "operationId": "record_benchmark_results_api_v1_benchmarks__run_id__results_post",
        "parameters": [
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResultBatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Recorded"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No run has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The run has ended, or the platform runs it and records its results"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Record Benchmark Results",
        "tags": [
          "Benchmarks"
        ]
      }
    },
    "/api/v1/config/": {
      "get": {
        "description": "List all configuration values. Sensitive values are redacted.",
        "operationId": "list_all_configs_api_v1_config__get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List All Configs Api V1 Config  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List All Configs",
        "tags": [
          "Configuration"
        ]
      },
      "head": {
        "description": "List all configuration values. Sensitive values are redacted.",
        "operationId": "list_all_configs_api_v1_config__head",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List All Configs Api V1 Config  Head",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List All Configs",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/all": {
      "get": {
        "description": "Get all configuration (for debugging). Sensitive values are NOT included.",
        "operationId": "get_all_config_api_v1_config_all_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get All Config Api V1 Config All Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get All Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/history": {
      "get": {
        "description": "The changes of spot.yaml, newest first: each one's author, message and sections.",
        "operationId": "list_history_api_v1_config_history_get",
        "parameters": [
          {
            "description": "Maximum commits to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum commits to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of commits to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of commits to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigHistoryPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List History",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/history/{commit_id}": {
      "get": {
        "description": "A commit, what it changed, and a preview of a rollback to it.\n\nThe preview is what a rollback to this commit would change in the\nsettings in force, as SPOT writes them.",
        "operationId": "get_commit_api_v1_config_history__commit_id__get",
        "parameters": [
          {
            "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
            "in": "path",
            "name": "commit_id",
            "required": true,
            "schema": {
              "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
              "pattern": "^[0-9a-f]{7,40}$",
              "title": "Commit Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitDetail"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No commit of the history has this id, or more than one starts with it"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Commit",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/history/{commit_id}/rollback": {
      "post": {
        "description": "Roll spot.yaml back to a commit: every section as it recorded it, but ``plugins:``.\n\nThe rollback is a change of its own, a commit by the user. Today's\nplugins stay as they are, and a restored workflow naming a plugin no\nlonger installed loads, its readiness broken. The analyzer-orchestrator\nruns the restored workflows from its next job; a platform setting read\nonly at start applies at the next restart.",
        "operationId": "roll_back_api_v1_config_history__commit_id__rollback_post",
        "parameters": [
          {
            "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
            "in": "path",
            "name": "commit_id",
            "required": true,
            "schema": {
              "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
              "pattern": "^[0-9a-f]{7,40}$",
              "title": "Commit Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RollbackResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No commit of the history has this id, or more than one starts with it"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The commit id is not 7 to 40 hex digits, or the commit's spot.yaml no longer validates: the message names each field and the rule it breaks, never a value; nothing is saved"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery); nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Roll Back",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/options/{path}": {
      "delete": {
        "description": "Reset a configuration option to its default value (persists to YAML).",
        "operationId": "reset_option_api_v1_config_options__path__delete",
        "parameters": [
          {
            "in": "path",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigOption"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Reset Option",
        "tags": [
          "Configuration"
        ]
      },
      "get": {
        "description": "Get a single configuration option by path.",
        "operationId": "get_option_endpoint_api_v1_config_options__path__get",
        "parameters": [
          {
            "in": "path",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigOption"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Option Endpoint",
        "tags": [
          "Configuration"
        ]
      },
      "put": {
        "description": "Update a single configuration option (persists to YAML).\n\nA change to an installed analyzer's settings restarts it before the\nplugin's lock is let go, and the answer, read under the same lock, says so.",
        "operationId": "update_option_api_v1_config_options__path__put",
        "parameters": [
          {
            "in": "path",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfigUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OptionUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No option can be set at this path: an unknown platform option, an analyzer that spot.yaml does not have, or a path under it that cannot be set"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, the value does not fit the option (an analyzer's settings are an object), or a setting is named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, is secret (set it with PUT /api/v1/config/plugin/analyzer/{id}), or has a value the plugin cannot read for its type or enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read, cannot read the plugin's entry, or does not list the installed plugin at its version (refresh the catalog, re-add its source, or update the plugin to a version it lists): nothing is saved. Or the restart failed, as when the plugin-manager is unavailable: the change stays saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Option",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/platform": {
      "get": {
        "description": "Get platform configuration settings.",
        "operationId": "get_platform_config_api_v1_config_platform_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlatformConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Platform Config",
        "tags": [
          "Configuration"
        ]
      },
      "put": {
        "description": "Update platform configuration settings (persists to YAML).",
        "operationId": "update_platform_config_api_v1_config_platform_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlatformConfigUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlatformConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Platform Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/mail_retriever/{plugin_id}/mode": {
      "put": {
        "description": "Switch a mail connection between observe and enforce.\n\nA job is in enforce only when its connection and its route both are. The\nretriever reads the mode in each answer to its ingests, so nothing is\nrestarted. The history records the switch as \"Set <id> to <mode>\".",
        "operationId": "set_connection_mode_api_v1_config_plugin_mail_retriever__plugin_id__mode_put",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginModeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No mail connection (mail_retriever) has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body is no mode: observe or enforce (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Set Connection Mode",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/{kind}": {
      "get": {
        "description": "List plugin IDs for a single kind.",
        "operationId": "list_by_kind_api_v1_config_plugin__kind__get",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List By Kind Api V1 Config Plugin  Kind  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List By Kind",
        "tags": [
          "Configuration"
        ]
      },
      "post": {
        "description": "Create a new plugin entry of the given kind; its secret settings are sealed.\n\nA bare entry, without settings or secrets, needs no catalog.",
        "operationId": "create_plugin_config_api_v1_config_plugin__kind__post",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginConfigCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The kind already has an entry of this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, or a setting named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, a secret name that is no safe file name, a url setting the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret), or a value the plugin cannot read for the setting's type, out of its minimum, maximum or pattern, or not one of its enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Settings or secrets were given and the catalog cannot be read, or cannot read its entry for the plugin (refresh the catalog); or spot.yaml cannot be saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Create Plugin Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/{kind}/{plugin_id}": {
      "delete": {
        "description": "Delete a plugin entry from the configuration, and revoke the plugin's token.",
        "operationId": "delete_plugin_config_api_v1_config_plugin__kind___plugin_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Plugin Config Api V1 Config Plugin  Kind   Plugin Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Plugin Config",
        "tags": [
          "Configuration"
        ]
      },
      "get": {
        "description": "Get configuration for a specific plugin.",
        "operationId": "get_plugin_config_api_v1_config_plugin__kind___plugin_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugin Config",
        "tags": [
          "Configuration"
        ]
      },
      "put": {
        "description": "Update configuration for a specific plugin (persists to YAML).\n\nNew settings replace the plain ones; those that are secret are sealed\nwith the given secrets, and the other stored secrets are kept. Each\nsetting is read as the plugin reads it, against its catalog schema, and\nstored as read. Saving the settings, the secrets or the url of an\ninstalled, enabled plugin restarts it (``apply_saved_settings``): the\nplugin is unavailable for a few seconds. The restart happens before the\nplugin's lock is let go, and the answer, read under the same lock, says\nso in ``restarting``. A disabled plugin takes the change when it is\nstarted; a manual entry is not restarted.",
        "operationId": "update_plugin_config_api_v1_config_plugin__kind___plugin_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginConfigUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The kind has no entry of this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, or a setting named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, a secret name that is no safe file name, a url setting the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret), or a value the plugin cannot read for the setting's type, out of its minimum, maximum or pattern, or not one of its enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read, cannot read the plugin's entry, or does not list the installed plugin at its version (refresh the catalog, re-add its source, or update the plugin to a version it lists), or spot.yaml cannot be saved: nothing is saved. Or the restart failed, as when the plugin-manager is unavailable: the change stays saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Plugin Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/{kind}/{plugin_id}/disable": {
      "post": {
        "description": "Disable a plugin.",
        "operationId": "disable_plugin_config_api_v1_config_plugin__kind___plugin_id__disable_post",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Disable Plugin Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/{kind}/{plugin_id}/enable": {
      "post": {
        "description": "Enable a plugin.",
        "operationId": "enable_plugin_config_api_v1_config_plugin__kind___plugin_id__enable_post",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Enable Plugin Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugin/{kind}/{plugin_id}/schema": {
      "get": {
        "description": "An installed plugin's settings schema, that of its version installed, as its source lists it.\n\nThe installer reads settings against the same schema. The running plugin\nis not asked, so the schema is there while it is down. Without a\nreadable entry of the version installed the route answers 503: refresh\nthe catalog, re-add the plugin's source, or update the plugin to a\nversion it lists.",
        "operationId": "get_plugin_schema_api_v1_config_plugin__kind___plugin_id__schema_get",
        "parameters": [
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/PluginKind"
            }
          },
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "$ref": "#/components/schemas/SettingField"
                  },
                  "title": "Response Get Plugin Schema Api V1 Config Plugin  Kind   Plugin Id  Schema Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The kind has no entry of this id, or the entry is a manual one"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read, cannot read the plugin's entry, or does not list the installed plugin at its version (refresh the catalog, re-add its source, or update the plugin to a version it lists)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugin Schema",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugins": {
      "get": {
        "description": "List all configured plugin IDs (optionally filtered by kind).",
        "operationId": "list_plugins_api_v1_config_plugins_get",
        "parameters": [
          {
            "description": "Optional kind filter",
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PluginKind"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Optional kind filter",
              "title": "Kind"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Plugins Api V1 Config Plugins Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Plugins",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/plugins/status": {
      "get": {
        "description": "Each plugin's own state, as the orchestrator reads it, with each analyzer's capabilities.\n\nEvery plugin of spot.yaml that has a URL, enabled or not, under its\nsection, with a summary of each section.",
        "operationId": "get_plugins_status_api_v1_config_plugins_status_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Plugins Status Api V1 Config Plugins Status Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugins Status",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/reload": {
      "post": {
        "description": "Reload configuration from YAML files, then seal plain secrets and sync the plugin tokens.\n\nspot.yaml's history is paused meanwhile; once the sealing step has\nended, it records the file as loaded and sealed, by the user. It\nrecords nothing when the file could not be loaded, or the reload was\ncancelled before (the settings in memory are recorded by the next save).\nThe token sync follows, the history resumed: it saves nothing to\nspot.yaml. Loads run one at a time (``AppState.load_lock``): a reload\nwaits for the start's sealing and token sync, or another reload, to end.",
        "operationId": "reload_config_api_v1_config_reload_post",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigReloadResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Reload Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/restart": {
      "post": {
        "description": "Restart the api-gateway container.\n\nUsed by the dashboard's Save-and-Restart flow when a setting requires\na process restart (log_level, rate-limiting, allowed_hosts, ...).\nThe handler returns 202 immediately and signals PID 1 (the\ncontainer's entrypoint) to exit on the next event-loop tick; Docker's\n`unless-stopped` policy brings the container back automatically.\n\nSignalling PID 1 (rather than ``os.getpid()``) is what makes this\nwork in both dev and prod: in prod uvicorn IS PID 1, in dev PID 1\nis the shell wrapper above the uvicorn ``--reload`` supervisor.\nEither way, killing PID 1 ends the container and Docker restarts\neverything from a clean state -- which is exactly what\nrestart-required settings need.",
        "operationId": "restart_api_gateway_api_v1_config_restart_post",
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Restart Api Gateway Api V1 Config Restart Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Restart Api Gateway",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/schema": {
      "get": {
        "description": "Get JSON Schema for configuration.",
        "operationId": "get_schema_api_v1_config_schema_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Schema Api V1 Config Schema Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Schema",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/status": {
      "get": {
        "description": "Check if configuration reload is needed.",
        "operationId": "get_status_api_v1_config_status_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigStatus"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Status",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/validate": {
      "post": {
        "description": "Validate a YAML config blob without persisting it.",
        "operationId": "validate_config_api_v1_config_validate_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Validate Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/config/workflows": {
      "get": {
        "description": "Get workflows configuration.",
        "operationId": "get_workflows_config_api_v1_config_workflows_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowsConfigResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflows Config",
        "tags": [
          "Configuration"
        ]
      }
    },
    "/api/v1/datasets": {
      "get": {
        "description": "The datasets, newest first, with their counts.",
        "operationId": "list_datasets_api_v1_datasets_get",
        "parameters": [
          {
            "description": "Maximum datasets to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum datasets to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of datasets to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of datasets to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Datasets",
        "tags": [
          "Datasets"
        ]
      },
      "post": {
        "description": "Create a dataset. Analysts and admins.",
        "operationId": "create_dataset_api_v1_datasets_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatasetIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dataset"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A dataset has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Create Dataset",
        "tags": [
          "Datasets"
        ]
      }
    },
    "/api/v1/datasets/{dataset_id}": {
      "delete": {
        "description": "Delete a dataset and its copies. Analysts and admins.\n\nThe emails the copies were made from are left alone.",
        "operationId": "delete_dataset_api_v1_datasets__dataset_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "dataset_id",
            "required": true,
            "schema": {
              "title": "Dataset Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetDeleted"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Dataset not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Dataset",
        "tags": [
          "Datasets"
        ]
      },
      "get": {
        "description": "A dataset, with its counts and a page of its copies: each one's subject and sender.",
        "operationId": "get_dataset_api_v1_datasets__dataset_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "dataset_id",
            "required": true,
            "schema": {
              "title": "Dataset Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum copies to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum copies to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of copies to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of copies to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetDetail"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Dataset not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Dataset",
        "tags": [
          "Datasets"
        ]
      }
    },
    "/api/v1/datasets/{dataset_id}/emails": {
      "post": {
        "description": "Copy a labelled received email into a dataset: its raw message, its view and its label.\n\nAnalysts and admins. The copy is kept out of the retention purge until it\nleaves the dataset; the original keeps its own expiry.",
        "operationId": "promote_email_api_v1_datasets__dataset_id__emails_post",
        "parameters": [
          {
            "in": "path",
            "name": "dataset_id",
            "required": true,
            "schema": {
              "title": "Dataset Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Promotion"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetMember"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Dataset not found, or Email not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email cannot be promoted: it has no label or no raw message, the dataset holds its message already or is full, or its stored view is unreadable"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Promote Email",
        "tags": [
          "Datasets"
        ]
      }
    },
    "/api/v1/datasets/{dataset_id}/emails/{email_id}": {
      "delete": {
        "description": "Remove a copy from a dataset, which deletes it. Analysts and admins.\n\n``email_id`` is the copy's id. The email it copies is left alone.",
        "operationId": "remove_email_api_v1_datasets__dataset_id__emails__email_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "dataset_id",
            "required": true,
            "schema": {
              "title": "Dataset Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The dataset holds no copy with this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Remove Email",
        "tags": [
          "Datasets"
        ]
      }
    },
    "/api/v1/emails": {
      "get": {
        "description": "List the summaries of stored emails, with pagination.\n\nA summary holds the email's id, label, subject, sender, source, raw size\nand dates, never its view or its body: ``/emails/{email_id}`` and\n``/emails/{email_id}/view`` give those.\n\n``recipient`` matches the first recipient of each email, the only one\nstored (as a hash); it is normalised the same way as on storage.",
        "operationId": "list_emails_api_v1_emails_get",
        "parameters": [
          {
            "description": "Filter by email source",
            "in": "query",
            "name": "source",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by email source",
              "title": "Source"
            }
          },
          {
            "description": "Search in subject and sender",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search in subject and sender",
              "title": "Search"
            }
          },
          {
            "description": "Filter by first recipient address",
            "in": "query",
            "name": "recipient",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "format": "email",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by first recipient address",
              "title": "Recipient"
            }
          },
          {
            "description": "Sort by column (created_at, source)",
            "in": "query",
            "name": "sort_by",
            "required": false,
            "schema": {
              "default": "created_at",
              "description": "Sort by column (created_at, source)",
              "title": "Sort By",
              "type": "string"
            }
          },
          {
            "description": "Sort order (asc, desc)",
            "in": "query",
            "name": "sort_order",
            "required": false,
            "schema": {
              "default": "desc",
              "description": "Sort order (asc, desc)",
              "title": "Sort Order",
              "type": "string"
            }
          },
          {
            "description": "Maximum items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum items to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of items to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Emails",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/emails/sources": {
      "get": {
        "description": "Get distinct email sources for filtering.\n\nReturns a list of unique source values across all stored emails.\nUseful for populating filter dropdowns.",
        "operationId": "get_email_sources_api_v1_emails_sources_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailSourcesResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Email Sources",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/emails/{email_id}": {
      "delete": {
        "description": "Delete an email (GDPR right to erasure).\n\nThis permanently deletes the email and any associated analysis results.\nThis action cannot be undone.",
        "operationId": "delete_email_api_v1_emails__email_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailDeleteResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Email",
        "tags": [
          "Emails"
        ]
      },
      "get": {
        "description": "Get a single email by ID.\n\nAnalysts and admins only. The email content (subject, body) is\ndecrypted on retrieval, from its whole view: the email counts in the\ngateway's bound on content served at once until it is sent (503 past\nit).\n\nSender and recipients are stored only as one-way hashes for PII protection,\nbut the repository reconstructs them from raw_headers when available;\nthey will be ``None`` for manual-form emails that never carried RFC\n5322 headers.\n\nThe response shape matches the schema served on the list endpoint\n(``dict[str, Any]``) so consumers see the same fields in both places\n-- the SDK's strict ``EmailResponse`` model lags by an SDK release on\nnew fields like ``sender``/``recipients``.",
        "operationId": "get_email_api_v1_emails__email_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Email Api V1 Emails  Email Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Email",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/emails/{email_id}/analyze": {
      "post": {
        "description": "Analyse a stored email again, without storing it again.\n\nAnalysts and admins only. The body is optional: JSON naming the workflow\nto run; without it, the email's route chooses it, on the stored view, or\nthe default workflow runs. The job takes the mode of the mail connection\nthe email came through (observe for a submission), and its route's when\nno workflow is named. A named workflow is not routed: the job takes the\nconnection's mode alone, so a route's observe does not hold it. It is\nqueued on the stored email as a submission's is, and the answer is the\nsame: a job ID to poll. The job is listed with the email's analyses\n(``GET /api/v1/analyze?email_id=``).\n\nAn unknown email is 404, and an email stored before 2.0, which has no raw\nmessage or view, 409.",
        "operationId": "reanalyze_email_api_v1_emails__email_id__analyze_post",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/AnalysisRequest"
                  },
                  {
                    "type": "null"
                  }
                ],
                "title": "Analysis"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "An email stored before 2.0, which has no raw message or view (conflict)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email id is not a UUID, or the body is invalid (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The job cannot be queued (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Reanalyze Email",
        "tags": [
          "Email Analysis"
        ]
      }
    },
    "/api/v1/emails/{email_id}/forensics": {
      "get": {
        "description": "Forensic derivations from the stored email.\n\nAnalysts and admins only. They are derived from the email's whole view,\ndecrypted: the email counts in the gateway's bound on content served at\nonce until the answer is sent (503 past it).\n\nSender / Reply-To / Return-Path domain alignment, Authentication-\nResults breakdown when the header is present, link table extracted\nfrom the body (text + HTML), attachment table with a hint flag for\nextensions classically used to deliver malware. All derivations are\npure functions of what is already stored — no outbound calls, no\nSPF/DKIM/DMARC re-evaluation.",
        "operationId": "get_email_forensics_api_v1_emails__email_id__forensics_get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Email Forensics Api V1 Emails  Email Id  Forensics Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Email Forensics",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/emails/{email_id}/label": {
      "delete": {
        "description": "Remove the label of an email. Analysts and admins.\n\nA dataset's copy keeps its label: it is relabelled, or removed from its\ndataset. The audit log records the class it removes, never the note.",
        "operationId": "delete_label_api_v1_emails__email_id__label_delete",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email has no label"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email is a dataset's copy and keeps its label: remove it from its dataset instead"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Label",
        "tags": [
          "Labels"
        ]
      },
      "get": {
        "description": "The label of an email, with its note. Analysts and admins.",
        "operationId": "get_label_api_v1_emails__email_id__label_get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Label"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email has no label"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Label",
        "tags": [
          "Labels"
        ]
      },
      "put": {
        "description": "Label an email, in place of its label: a class, never unknown, and a note.\n\nAnalysts and admins. The audit log records the class and the class it\nreplaces, never the note, which is stored encrypted.",
        "operationId": "put_label_api_v1_emails__email_id__label_put",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LabelIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Label"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Email not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The class is unknown, which no label is, or the note is over 2000 characters"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Put Label",
        "tags": [
          "Labels"
        ]
      }
    },
    "/api/v1/emails/{email_id}/raw": {
      "get": {
        "description": "The email's raw message, as received (``message/rfc822``), to save as ``<email_id>.eml``.\n\nAnalysts and admins only; each read is recorded in the audit log\n(``email.raw``), without the content. An email stored before 2.0 has no raw\ncopy (409). The message counts in the gateway's bound on content served at\nonce until it is sent (503 past it).",
        "operationId": "get_email_raw_api_v1_emails__email_id__raw_get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "message/rfc822": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The message as received"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "An email stored before 2.0, which has no raw message or view (conflict)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email id is not a UUID (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Email Raw",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/emails/{email_id}/view": {
      "get": {
        "description": "The email's view (``spot_sdk.EmailView``, by alias), as analyzers receive it.\n\nAnalysts and admins only. An email stored before 2.0 has no view (409).\nThe view, up to 8 MiB, counts in the gateway's bound on content served at\nonce until it is sent (503 past it).",
        "operationId": "get_email_view_api_v1_emails__email_id__view_get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailView"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "An email stored before 2.0, which has no raw message or view (conflict)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email id is not a UUID (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Email View",
        "tags": [
          "Emails"
        ]
      }
    },
    "/api/v1/knowledge/documents": {
      "get": {
        "operationId": "list_documents_api_v1_knowledge_documents_get",
        "parameters": [
          {
            "in": "query",
            "name": "tags",
            "required": false,
            "schema": {
              "default": "",
              "title": "Tags",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "source",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Source"
            }
          },
          {
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Q"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "title": "Offset",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Documents Api V1 Knowledge Documents Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Documents",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/knowledge/query": {
      "post": {
        "operationId": "query_api_v1_knowledge_query_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "title": "Body",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Query Api V1 Knowledge Query Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Query",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/knowledge/readiness": {
      "get": {
        "description": "Composite readiness check for the knowledge subsystem.\n\nCombines the knowledge service's own ``/readiness`` (which probes\nthe embedding backend) with api-gateway-side facts — installed\ncontext providers, their enabled state — so the dashboard can\nrender a single coherent warning banner instead of stitching\nthree queries itself.",
        "operationId": "readiness_api_v1_knowledge_readiness_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Readiness Api V1 Knowledge Readiness Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Readiness",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/knowledge/sources": {
      "get": {
        "operationId": "sources_api_v1_knowledge_sources_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Sources Api V1 Knowledge Sources Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Sources",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/knowledge/sources/{source}/documents/{doc_id}": {
      "delete": {
        "description": "Delete a document of the source; the audit log names it ``<source>/<id>``.",
        "operationId": "delete_document_api_v1_knowledge_sources__source__documents__doc_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "source",
            "required": true,
            "schema": {
              "title": "Source",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "doc_id",
            "required": true,
            "schema": {
              "title": "Doc Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Document Api V1 Knowledge Sources  Source  Documents  Doc Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Document",
        "tags": [
          "Knowledge Store"
        ]
      },
      "get": {
        "operationId": "get_document_api_v1_knowledge_sources__source__documents__doc_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "source",
            "required": true,
            "schema": {
              "title": "Source",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "doc_id",
            "required": true,
            "schema": {
              "title": "Doc Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Document Api V1 Knowledge Sources  Source  Documents  Doc Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Document",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/knowledge/stats": {
      "get": {
        "operationId": "stats_api_v1_knowledge_stats_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Stats Api V1 Knowledge Stats Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Stats",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/labels": {
      "get": {
        "description": "The labels, newest first, each with the sender of its email, and its dataset for a copy.\n\nAnalysts and admins: the labels name senders and carry notes. A copy's\nlabel is listed apart from its original's: ``dataset_id`` tells them apart.",
        "operationId": "list_labels_api_v1_labels_get",
        "parameters": [
          {
            "description": "Only this class",
            "in": "query",
            "name": "verdict",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "malicious",
                    "suspicious",
                    "spam",
                    "benign"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only this class",
              "title": "Verdict"
            }
          },
          {
            "description": "Only the labels set from this moment",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "format": "date-time",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only the labels set from this moment",
              "title": "Since"
            }
          },
          {
            "description": "Maximum labels to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum labels to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of labels to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of labels to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LabelPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Labels",
        "tags": [
          "Labels"
        ]
      }
    },
    "/api/v1/mail-retrievers/jobs": {
      "get": {
        "description": "Most recent ingestion jobs across all retrievers (newest first).",
        "operationId": "list_recent_jobs_api_v1_mail_retrievers_jobs_get",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Recent Jobs Api V1 Mail Retrievers Jobs Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Recent Jobs",
        "tags": [
          "Mail Retrievers"
        ]
      },
      "head": {
        "description": "Most recent ingestion jobs across all retrievers (newest first).",
        "operationId": "list_recent_jobs_api_v1_mail_retrievers_jobs_head",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Recent Jobs Api V1 Mail Retrievers Jobs Head",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Recent Jobs",
        "tags": [
          "Mail Retrievers"
        ]
      }
    },
    "/api/v1/mail-retrievers/jobs/{job_id}": {
      "get": {
        "description": "Single ingestion job by id (transient — TTL applies).",
        "operationId": "get_job_api_v1_mail_retrievers_jobs__job_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Job Api V1 Mail Retrievers Jobs  Job Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Job",
        "tags": [
          "Mail Retrievers"
        ]
      },
      "head": {
        "description": "Single ingestion job by id (transient — TTL applies).",
        "operationId": "get_job_api_v1_mail_retrievers_jobs__job_id__head",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Job Api V1 Mail Retrievers Jobs  Job Id  Head",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Job",
        "tags": [
          "Mail Retrievers"
        ]
      }
    },
    "/api/v1/mail-retrievers/metrics": {
      "get": {
        "description": "Cumulative counters: total, errors, and analysed ingestions by verdict class.",
        "operationId": "get_metrics_api_v1_mail_retrievers_metrics_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "integer"
                  },
                  "title": "Response Get Metrics Api V1 Mail Retrievers Metrics Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Metrics",
        "tags": [
          "Mail Retrievers"
        ]
      },
      "head": {
        "description": "Cumulative counters: total, errors, and analysed ingestions by verdict class.",
        "operationId": "get_metrics_api_v1_mail_retrievers_metrics_head",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "integer"
                  },
                  "title": "Response Get Metrics Api V1 Mail Retrievers Metrics Head",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Metrics",
        "tags": [
          "Mail Retrievers"
        ]
      }
    },
    "/api/v1/observability/queues": {
      "get": {
        "description": "RabbitMQ queue and exchange overview via the management API.",
        "operationId": "queues_overview_api_v1_observability_queues_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Queues Overview Api V1 Observability Queues Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Queues Overview",
        "tags": [
          "Observability"
        ]
      }
    },
    "/api/v1/observability/queues/{queue}/replay": {
      "post": {
        "description": "Move messages parked in the queue's dead-letter queue back into it (admins).\n\nFix what made them fail first, then replay one (``limit=1``) and check\nthat it is handled before replaying the rest: a message that fails\nagain holds one of the queue's slots through its retries, about 13.5\nminutes, before it is parked again. Only the messages parked when the\nreplay starts move, each at least once.",
        "operationId": "replay_queue_api_v1_observability_queues__queue__replay_post",
        "parameters": [
          {
            "in": "path",
            "name": "queue",
            "required": true,
            "schema": {
              "title": "Queue",
              "type": "string"
            }
          },
          {
            "description": "Move at most this many; absent, every message parked when the replay starts",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "minimum": 1,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Move at most this many; absent, every message parked when the replay starts",
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueReplayed"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No queue of this name parks messages"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "limit is below 1 (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message broker is not available, or failed during the replay: what was not moved stays parked; replay again"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Replay Queue",
        "tags": [
          "Observability"
        ]
      }
    },
    "/api/v1/observability/services": {
      "get": {
        "description": "Per-service health rollup across the four platform services.",
        "operationId": "services_health_api_v1_observability_services_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Services Health Api V1 Observability Services Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Services Health",
        "tags": [
          "Observability"
        ]
      }
    },
    "/api/v1/plugin-catalog": {
      "get": {
        "description": "List available plugins from the catalog.\n\nSupports filtering by source, kind, capability, and compatibility.",
        "operationId": "list_catalog_api_v1_plugin_catalog_get",
        "parameters": [
          {
            "description": "Filter by source ID",
            "in": "query",
            "name": "source_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by source ID",
              "title": "Source Id"
            }
          },
          {
            "description": "Filter by plugin kind",
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PluginKind"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by plugin kind",
              "title": "Kind"
            }
          },
          {
            "description": "Filter by capability",
            "in": "query",
            "name": "capability",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by capability",
              "title": "Capability"
            }
          },
          {
            "description": "Only return compatible plugins",
            "in": "query",
            "name": "compatible_only",
            "required": false,
            "schema": {
              "default": false,
              "description": "Only return compatible plugins",
              "title": "Compatible Only",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Catalog",
        "tags": [
          "Plugin Catalog"
        ]
      }
    },
    "/api/v1/plugin-catalog/refresh": {
      "post": {
        "description": "Refresh the plugin catalog by scanning sources.",
        "operationId": "refresh_catalog_api_v1_plugin_catalog_refresh_post",
        "parameters": [
          {
            "description": "Refresh a specific source",
            "in": "query",
            "name": "source_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Refresh a specific source",
              "title": "Source Id"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanResultResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Refresh Catalog",
        "tags": [
          "Plugin Catalog"
        ]
      }
    },
    "/api/v1/plugin-catalog/{plugin_id}": {
      "get": {
        "description": "Get details of a specific catalog plugin; of the first source listing it unless one is given.",
        "operationId": "get_plugin_api_v1_plugin_catalog__plugin_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          },
          {
            "description": "The source to read it from",
            "in": "query",
            "name": "source_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "The source to read it from",
              "title": "Source Id"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginInfoResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugin",
        "tags": [
          "Plugin Catalog"
        ]
      }
    },
    "/api/v1/plugin-catalog/{plugin_id}/check-update": {
      "post": {
        "description": "Re-scan the source that owns ``plugin_id`` and return the refreshed entry.\n\nUsed by the dashboard's per-plugin \"Check update\" affordance: the\ndetail page's button calls this route so the operator can confirm\nwhether a newer version is available without scanning every\nregistered source. When two sources list the plugin, ``source_id``\ntells which one is re-scanned.",
        "operationId": "check_plugin_update_api_v1_plugin_catalog__plugin_id__check_update_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          },
          {
            "description": "The source to re-scan",
            "in": "query",
            "name": "source_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "The source to re-scan",
              "title": "Source Id"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginInfoResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Check Plugin Update",
        "tags": [
          "Plugin Catalog"
        ]
      }
    },
    "/api/v1/plugin-catalog/{plugin_id}/readme": {
      "get": {
        "description": "The README.md of a plugin's repository at its version's tag.\n\nThe text is the plugin author's, returned as written: whoever shows it\nrenders it as untrusted. The repository is the catalog entry's,\nwhatever the version.",
        "operationId": "get_plugin_readme_api_v1_plugin_catalog__plugin_id__readme_get",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          },
          {
            "description": "The source to read it from",
            "in": "query",
            "name": "source_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "The source to read it from",
              "title": "Source Id"
            }
          },
          {
            "description": "A version the source lists; its newest unless given",
            "in": "query",
            "name": "version",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "A version the source lists; its newest unless given",
              "title": "Version"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginReadme"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin or version is not listed, or its repository has no README"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog or the forge cannot be read, the source's token cannot be decrypted, or the entry names a forge other than its source's: refresh the catalog"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugin Readme",
        "tags": [
          "Plugin Catalog"
        ]
      }
    },
    "/api/v1/plugin-sources": {
      "get": {
        "description": "List all configured plugin sources.\n\nArgs:\n    enabled_only: If True, only return enabled sources\n\nReturns:\n    SourceListResponse with sources and total count",
        "operationId": "list_sources_api_v1_plugin_sources_get",
        "parameters": [
          {
            "in": "query",
            "name": "enabled_only",
            "required": false,
            "schema": {
              "default": false,
              "title": "Enabled Only",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Sources",
        "tags": [
          "Plugin Sources"
        ]
      },
      "post": {
        "description": "Create a new plugin source.\n\nArgs:\n    request: Source creation request\n\nReturns:\n    Created source\n\nRaises:\n    HTTPException: 400 if source already exists, 503 if registry unavailable",
        "operationId": "create_source_api_v1_plugin_sources_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Create Source",
        "tags": [
          "Plugin Sources"
        ]
      }
    },
    "/api/v1/plugin-sources/{source_id}": {
      "delete": {
        "description": "Delete an plugin source.\n\nArgs:\n    source_id: Source identifier\n\nReturns:\n    Success message\n\nRaises:\n    HTTPException: 404 if source not found, 503 if registry unavailable",
        "operationId": "delete_source_api_v1_plugin_sources__source_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Source",
        "tags": [
          "Plugin Sources"
        ]
      },
      "get": {
        "description": "Get details of a specific plugin source.\n\nArgs:\n    source_id: Source identifier\n\nReturns:\n    Source details\n\nRaises:\n    HTTPException: 404 if source not found",
        "operationId": "get_source_api_v1_plugin_sources__source_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Source",
        "tags": [
          "Plugin Sources"
        ]
      },
      "put": {
        "description": "Update an plugin source.\n\nArgs:\n    source_id: Source identifier\n    request: Update request\n\nReturns:\n    Updated source\n\nRaises:\n    HTTPException: 404 if source not found, 422 if the body or the source that\n        results is invalid, 503 if registry unavailable",
        "operationId": "update_source_api_v1_plugin_sources__source_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body, or the source that results, is invalid (validation_error): the rules it breaks"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Source",
        "tags": [
          "Plugin Sources"
        ]
      }
    },
    "/api/v1/plugin-sources/{source_id}/disable": {
      "post": {
        "description": "Disable an plugin source.\n\nArgs:\n    source_id: Source identifier\n\nReturns:\n    Updated source\n\nRaises:\n    HTTPException: 404 if source not found, 503 if registry unavailable",
        "operationId": "disable_source_api_v1_plugin_sources__source_id__disable_post",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Disable Source",
        "tags": [
          "Plugin Sources"
        ]
      }
    },
    "/api/v1/plugin-sources/{source_id}/enable": {
      "post": {
        "description": "Enable an plugin source.\n\nArgs:\n    source_id: Source identifier\n\nReturns:\n    Updated source\n\nRaises:\n    HTTPException: 404 if source not found, 503 if registry unavailable",
        "operationId": "enable_source_api_v1_plugin_sources__source_id__enable_post",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SourceResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Enable Source",
        "tags": [
          "Plugin Sources"
        ]
      }
    },
    "/api/v1/plugin-sources/{source_id}/scan": {
      "post": {
        "description": "Scan a source for available plugins.\n\nTriggers a discovery scan on the specified source and updates the catalog.\n\nArgs:\n    source_id: Source identifier\n\nReturns:\n    Scan results including discovered plugins\n\nRaises:\n    HTTPException: 404 if source not found, 503 if scan fails",
        "operationId": "scan_source_api_v1_plugin_sources__source_id__scan_post",
        "parameters": [
          {
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanResultResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Scan Source",
        "tags": [
          "Plugin Sources"
        ]
      }
    },
    "/api/v1/plugins/context_provider/{provider_id}/sync": {
      "get": {
        "operationId": "last_sync_api_v1_plugins_context_provider__provider_id__sync_get",
        "parameters": [
          {
            "in": "path",
            "name": "provider_id",
            "required": true,
            "schema": {
              "title": "Provider Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Last Sync Api V1 Plugins Context Provider  Provider Id  Sync Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Last Sync",
        "tags": [
          "Knowledge Store"
        ]
      },
      "post": {
        "operationId": "trigger_sync_api_v1_plugins_context_provider__provider_id__sync_post",
        "parameters": [
          {
            "in": "path",
            "name": "provider_id",
            "required": true,
            "schema": {
              "title": "Provider Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Trigger Sync Api V1 Plugins Context Provider  Provider Id  Sync Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Trigger Sync",
        "tags": [
          "Knowledge Store"
        ]
      }
    },
    "/api/v1/plugins/install": {
      "post": {
        "description": "Install a plugin from the catalog; one of egress class content needs consent.",
        "operationId": "install_plugin_api_v1_plugins_install_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginInstallRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstallResultResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is installed already, no version of it runs on this SPOT, or its source does not list the version asked for (the reason is in the message)"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog does not list the plugin (in that source, when one is given)"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin's egress class is content and the body has no consent_to_content"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, or a setting named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, a secret name that is no safe file name, a url setting the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret), or a value the plugin cannot read for the setting's type, out of its minimum, maximum or pattern, or not one of its enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read (or the index kept for the plugin's source no longer checks), the plugin-manager or Docker is unavailable, or spot.yaml cannot be saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Install Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/installed": {
      "get": {
        "description": "List all installed plugins (optionally filtered by kind).\n\nEach carries its container's status and health, and its own state\n(``plugin_health``): the plugins' are read all at once.",
        "operationId": "list_installed_api_v1_plugins_installed_get",
        "parameters": [
          {
            "description": "Filter by plugin kind",
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PluginKind"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by plugin kind",
              "title": "Kind"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstalledListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Installed",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/installed/{plugin_id}": {
      "get": {
        "description": "Get details of an installed plugin.",
        "operationId": "get_installed_api_v1_plugins_installed__plugin_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstalledPluginResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Installed",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}": {
      "delete": {
        "description": "Uninstall a plugin.",
        "operationId": "uninstall_plugin_api_v1_plugins__plugin_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Uninstall Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/resources": {
      "delete": {
        "description": "Remove an installed plugin's resources override, then restart the plugin.\n\nThe plugin runs with the memory and CPUs its image asked for, else\n1024 MB and 1 CPU. It is restarted as by ``PUT``, its override put\nback when it cannot be restarted without it, and the answers are the\nsame.",
        "operationId": "reset_resources_api_v1_plugins__plugin_id__resources_delete",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstalledPluginResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Docker refused the new limits, with its reason, such as more CPUs than the host has: the previous ones are saved again, and the plugin runs with them"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be saved: nothing is saved. Or the restart failed, as when the plugin-manager is unavailable: the previous limits are saved again, and the plugin restarted with them when it can be"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Reset Resources",
        "tags": [
          "Plugin Installation"
        ]
      },
      "put": {
        "description": "Override an installed plugin's memory, its CPUs or both, then restart the plugin.\n\nA value left out keeps the plugin's effective one: its override, else\nthe memory and CPUs its image asked for, else 1024 MB and 1 CPU. An\nenabled plugin is restarted: its container is re-created with the new\nlimits, and the plugin is unavailable for a few seconds; a disabled\nplugin takes them when it is started. The answer shows the override\n(``resources``), the image's request (``requested_resources``), the\nlimits in force (``effective_resources``) and ``restarting``.\n\nA plugin that cannot be restarted with the new limits gets its\nprevious ones back, saved and restarted with them: Docker's refusal of\nthe limits, such as more CPUs than the host has, answers 422 with its\nreason, and any other failure 503.",
        "operationId": "set_resources_api_v1_plugins__plugin_id__resources_put",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginResourcesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstalledPluginResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate: it gives neither memory_mb nor cpus, memory_mb is under 128, cpus is 0 or less or over 64, or it has another key. Or Docker refused the new limits, with its reason, such as more CPUs than the host has: the previous ones are saved again, and the plugin runs with them"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be saved: nothing is saved. Or the restart failed, as when the plugin-manager is unavailable: the previous limits are saved again, and the plugin restarted with them when it can be"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Set Resources",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/restart": {
      "post": {
        "description": "Restart an installed plugin: its container is re-created from spot.yaml when the spec\nchanged (settings, secrets, resources, image), else restarted in place. Answers 404 for a\nplugin that is not installed, and 409 for a disabled one: it is enabled first.",
        "operationId": "restart_plugin_api_v1_plugins__plugin_id__restart_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is disabled: enable it first"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin-manager or Docker is unavailable or refuses (a container of its name SPOT does not manage); for start and restart, the plugin's secrets cannot be decrypted, or, in an entry saved by 2.0 or edited by hand, a url setting is one the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret); or spot.yaml cannot be saved (the enabled state for start and stop, a re-created container's id for start and restart)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Restart Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/settings": {
      "put": {
        "description": "Update plugin settings and secrets, then restart the plugin so it takes them.\n\nThe settings given are merged into the current ones. Each is read as the\nplugin reads its text, against the plugin's catalog schema: for its type\n(``\"8000\"`` and ``8000`` are an integer, ``\"true\"`` and ``true`` a\nboolean), then its ``enum``, and stored as read; a list or an object is\nrefused. A setting the schema does not declare is stored as given.\nSecrets are sealed in spot.yaml and reach the plugin as files\n``/run/secrets/<NAME>``. A setting the schema marks secret, or stored as\na secret, is sealed even when sent in ``settings``, and an empty secret\n(``\"\"`` or null) keeps the stored one. The answer names the secrets,\nnever a value.\n\nAn enabled plugin is restarted: its container is re-created from\nspot.yaml, and the plugin is unavailable for a few seconds. The save, the\nrestart and the answer are one step for the plugin: a stop or an\nuninstall sent meanwhile waits for them. The answer shows the plugin\nafter the restart, with ``restarting``; a disabled plugin takes the\nsettings when it is started.",
        "operationId": "update_settings_api_v1_plugins__plugin_id__settings_put",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginSettingsUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstalledPluginResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate (a key other than settings and secrets is refused), or a setting named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, a secret name that is no safe file name, a url setting the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret), or a value the plugin cannot read for the setting's type, out of its minimum, maximum or pattern, or not one of its enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read, cannot read the plugin's entry, or does not list the installed plugin at its version (refresh the catalog, re-add its source, or update the plugin to a version it lists), or spot.yaml cannot be saved: nothing is saved. Or the restart failed, as when the plugin-manager is unavailable: the settings stay saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Settings",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/start": {
      "post": {
        "description": "Start a plugin container.",
        "operationId": "start_plugin_api_v1_plugins__plugin_id__start_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin-manager or Docker is unavailable or refuses (a container of its name SPOT does not manage); for start and restart, the plugin's secrets cannot be decrypted, or, in an entry saved by 2.0 or edited by hand, a url setting is one the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret); or spot.yaml cannot be saved (the enabled state for start and stop, a re-created container's id for start and restart)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Start Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/status": {
      "get": {
        "description": "A plugin's container status and health, and its own state from its ``/health``.\n\nWorks for any plugin in spot.yaml: a manual entry has no container, so\nits status is ``external``, and its own state is read when it has a URL.",
        "operationId": "get_plugin_status_api_v1_plugins__plugin_id__status_get",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginStatusResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Plugin Status",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/stop": {
      "post": {
        "description": "Stop a plugin container.",
        "operationId": "stop_plugin_api_v1_plugins__plugin_id__stop_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin-manager or Docker is unavailable or refuses (a container of its name SPOT does not manage); for start and restart, the plugin's secrets cannot be decrypted, or, in an entry saved by 2.0 or edited by hand, a url setting is one the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret); or spot.yaml cannot be saved (the enabled state for start and stop, a re-created container's id for start and restart)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Stop Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/token": {
      "post": {
        "description": "Issue a plugin a new token and return it, once; the old one stops working.\n\nWorks for any plugin in spot.yaml. An installed plugin's container is\nrecreated with the new token; a manual plugin's operator delivers it.",
        "operationId": "issue_token_api_v1_plugins__plugin_id__token_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PluginTokenResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No entry of spot.yaml has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin's secrets cannot be decrypted: it keeps its token. Or the container could not be re-created, as when a url setting is one the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret): the new token is stored, and the container keeps the revoked one until a restart of the plugin, or the api-gateway's next start, gives it the new one"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Issue Token",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/plugins/{plugin_id}/update": {
      "post": {
        "description": "Update a plugin to the version asked for, else to the newest this SPOT runs if newer; egress class content needs consent.\n\nThe version comes from the plugin's source, or from ``source_id``, which\nmoves the plugin to that source, at the version installed when it lists\nnone newer that runs.",
        "operationId": "update_plugin_api_v1_plugins__plugin_id__update_post",
        "parameters": [
          {
            "in": "path",
            "name": "plugin_id",
            "required": true,
            "schema": {
              "title": "Plugin Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PluginUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstallResultResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The plugin is not installed, the catalog no longer lists it (in the source asked for, when one is given), or the source does not list the version asked for, or the installed one when settings or another source are given without a version"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No version runs on this SPOT, or the version asked for does not, or is refused (its reason), is of another kind, or its egress class is content with no consent recorded and no consent_to_content"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body does not validate, or a setting named spot_plugin_token or spot_proxy_credentials, which are reserved for the plugin's token and the proxy credentials, a secret name that is no safe file name, a url setting the egress gateway cannot serve (an address, no host or port, a second host on a port, the bundled Ollama for a class other than content, or a secret), or a value the plugin cannot read for the setting's type, out of its minimum, maximum or pattern, or not one of its enum: the setting and the rule are named, never the value"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The catalog cannot be read (or the index kept for the plugin's source no longer checks), the plugin-manager or Docker is unavailable, or spot.yaml cannot be saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Plugin",
        "tags": [
          "Plugin Installation"
        ]
      }
    },
    "/api/v1/routing": {
      "get": {
        "description": "The routing document: the recipient groups, the routes in order and the shadow entries.",
        "operationId": "get_routing_api_v1_routing_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingDocument"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Routing",
        "tags": [
          "Routing"
        ]
      },
      "put": {
        "description": "Replace the routing document, as saved; an empty one clears the routing.\n\nThe routes take effect for the next email. The history records the\nchange as \"Change routing\", by the user.",
        "operationId": "put_routing_api_v1_routing_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoutingDocument"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingDocument"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The document breaks a rule (validation_error), which the message names: a route or shadow entry naming a workflow that is not loaded, or sender_domains without mail.trusted_authserv_ids; or the errors list the fields (an unknown group, a route named twice, a malformed entry). Nothing is saved"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Put Routing",
        "tags": [
          "Routing"
        ]
      }
    },
    "/api/v1/sender-lists": {
      "get": {
        "description": "The block and allow lists of sender domains.",
        "operationId": "get_sender_lists_api_v1_sender_lists_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SenderLists"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Sender Lists",
        "tags": [
          "Sender lists"
        ]
      }
    },
    "/api/v1/sender-lists/{which}": {
      "post": {
        "description": "Add a domain to the block or the allow list, as saved.\n\nThe lists take effect for the next email. The history records the change\nas \"Block <domain>\" or \"Allow <domain>\", with the email of the label it\ncomes from when the request names one, by the user.",
        "operationId": "add_sender_domain_api_v1_sender_lists__which__post",
        "parameters": [
          {
            "in": "path",
            "name": "which",
            "required": true,
            "schema": {
              "enum": [
                "allow",
                "block"
              ],
              "title": "Which",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SenderDomainIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SenderLists"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The domain is on a sender list already, this one or the other"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The domain is no domain, or the allow list is asked while mail.trusted_authserv_ids is empty. Nothing is saved"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Add Sender Domain",
        "tags": [
          "Sender lists"
        ]
      }
    },
    "/api/v1/sender-lists/{which}/{domain}": {
      "delete": {
        "description": "Remove a domain from the block or the allow list, as saved.\n\nThe history records the change as \"Remove <domain> from the <list> list\",\nby the user.",
        "operationId": "remove_sender_domain_api_v1_sender_lists__which___domain__delete",
        "parameters": [
          {
            "in": "path",
            "name": "which",
            "required": true,
            "schema": {
              "enum": [
                "allow",
                "block"
              ],
              "title": "Which",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "domain",
            "required": true,
            "schema": {
              "title": "Domain",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SenderLists"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The domain is not on that list"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The domain is no domain (example.com) or its subdomains (*.example.com)"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Remove Sender Domain",
        "tags": [
          "Sender lists"
        ]
      }
    },
    "/api/v1/system/health": {
      "get": {
        "description": "Aggregate health across platform services and plugins.\n\nEach plugin's entry is its kind and its own state, ``status`` and\n``reason``; a plugin that is ``starting`` counts as degraded.",
        "operationId": "system_health_api_v1_system_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response System Health Api V1 System Health Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "System Health",
        "tags": [
          "System"
        ]
      }
    },
    "/api/v1/system/updates": {
      "get": {
        "operationId": "get_updates_api_v1_system_updates_get",
        "parameters": [
          {
            "description": "Bypass the 1-hour cache and re-fetch from Codeberg",
            "in": "query",
            "name": "force",
            "required": false,
            "schema": {
              "default": false,
              "description": "Bypass the 1-hour cache and re-fetch from Codeberg",
              "title": "Force",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemUpdateResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Updates",
        "tags": [
          "System"
        ]
      }
    },
    "/api/v1/system/version": {
      "get": {
        "description": "Aggregate version across platform services and plugins.",
        "operationId": "system_version_api_v1_system_version_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response System Version Api V1 System Version Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "System Version",
        "tags": [
          "System"
        ]
      }
    },
    "/api/v1/users": {
      "get": {
        "description": "List all users with pagination and filtering.\n\nAdmin only.",
        "operationId": "list_users_api_v1_users_get",
        "parameters": [
          {
            "description": "Search by username or email",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 100,
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search by username or email",
              "title": "Search"
            }
          },
          {
            "description": "Filter by role",
            "in": "query",
            "name": "role",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/UserRole"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by role",
              "title": "Role"
            }
          },
          {
            "description": "Filter by active status",
            "in": "query",
            "name": "is_active",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by active status",
              "title": "Is Active"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Users",
        "tags": [
          "User Management"
        ]
      },
      "post": {
        "description": "Create a new user.\n\nAdmin only.",
        "operationId": "create_user_api_v1_users_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Create User",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/users/me": {
      "get": {
        "description": "Get current user's profile.\n\nAny authenticated user.",
        "operationId": "get_current_user_profile_api_v1_users_me_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Current User Profile",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/users/{user_id}": {
      "delete": {
        "description": "Soft-delete (deactivate) a user.\n\nAdmin only. Cannot delete yourself or the last admin.",
        "operationId": "delete_user_api_v1_users__user_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete User",
        "tags": [
          "User Management"
        ]
      },
      "get": {
        "description": "Get user by ID.\n\nAdmin can view any user. Users can view themselves.",
        "operationId": "get_user_api_v1_users__user_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get User",
        "tags": [
          "User Management"
        ]
      },
      "put": {
        "description": "Update user details.\n\nAdmin only.",
        "operationId": "update_user_api_v1_users__user_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update User",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/users/{user_id}/activate": {
      "post": {
        "description": "Reactivate a deactivated user.\n\nAdmin only.",
        "operationId": "activate_user_api_v1_users__user_id__activate_post",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Activate User",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/users/{user_id}/password": {
      "put": {
        "description": "Change user password.\n\nAdmin can change any user's password without current password.\nUsers can change their own password (requires current password).",
        "operationId": "update_user_password_api_v1_users__user_id__password_put",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserPasswordUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update User Password",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/users/{user_id}/role": {
      "put": {
        "description": "Change user role.\n\nAdmin only. Cannot demote yourself or the last admin.",
        "operationId": "update_user_role_api_v1_users__user_id__role_put",
        "parameters": [
          {
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "title": "User Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserRoleUpdateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update User Role",
        "tags": [
          "User Management"
        ]
      }
    },
    "/api/v1/workflows": {
      "get": {
        "description": "List all available workflows with optional filtering and pagination.\n\nArgs:\n    search: Optional search term to filter by name (case-insensitive).\n    limit: Maximum number of workflows to return (1-100).\n    offset: Number of workflows to skip for pagination.",
        "operationId": "list_workflows_api_v1_workflows_get",
        "parameters": [
          {
            "description": "Search by workflow name",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search by workflow name",
              "title": "Search"
            }
          },
          {
            "description": "Maximum items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum items to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of items to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator answers 500, or the call to it fails otherwise; the message says what"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator refused the gateway's internal key"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator cannot be reached"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator does not answer in time"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Workflows",
        "tags": [
          "Workflow Management"
        ]
      },
      "head": {
        "description": "List all available workflows with optional filtering and pagination.\n\nArgs:\n    search: Optional search term to filter by name (case-insensitive).\n    limit: Maximum number of workflows to return (1-100).\n    offset: Number of workflows to skip for pagination.",
        "operationId": "list_workflows_api_v1_workflows_head",
        "parameters": [
          {
            "description": "Search by workflow name",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Search by workflow name",
              "title": "Search"
            }
          },
          {
            "description": "Maximum items to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum items to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of items to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of items to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Workflows",
        "tags": [
          "Workflow Management"
        ]
      },
      "post": {
        "description": "Create a new workflow: validated, then saved into spot.yaml.\n\nAn id spot.yaml stores is taken, one left out at load included: PUT\nreplaces it. The gateway numbers its version: 1 + the highest version\nits id ever had (1 for a new id); its author is the user. The body's\n``version`` and ``created_by`` are dropped, whatever they hold. The\nanalyzer-orchestrator runs it from its next job, once it has read\nspot.yaml again.",
        "operationId": "create_workflow_api_v1_workflows_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "title": "Workflow Data",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Create Workflow Api V1 Workflows Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow breaks a rule (bad_request): the message names it, and names the stage or the field"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A workflow has this id already, one left out at load included: PUT replaces it"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery); nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Create Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/default": {
      "get": {
        "description": "Return the id of the workflow used when none is specified.\n\nReads from spot.yaml (with the resolution fallback chain in\nSpotSettings.get_default_workflow_id()). Cheap; safe to call from\ndetail pages that need the is_default flag without re-pulling the\nfull workflow list.\n\nDefined before ``/workflows/{workflow_id}`` so ``\"default\"`` doesn't\nget matched as a workflow id.",
        "operationId": "get_default_workflow_api_v1_workflows_default_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Default Workflow Api V1 Workflows Default Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Default Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/readiness": {
      "get": {
        "description": "Whether each workflow can run now (ready, degraded or broken).\n\nComputed by the analyzer-orchestrator from the health of the analyzers\neach workflow uses. Readable by every role, unlike the plugin status\nit is derived from. Defined before ``/workflows/{workflow_id}``.",
        "operationId": "get_workflow_readiness_api_v1_workflows_readiness_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Workflow Readiness Api V1 Workflows Readiness Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator answers 500, or the call to it fails otherwise; the message says what"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator refused the gateway's internal key"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator cannot be reached"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator does not answer in time"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow Readiness",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/validate": {
      "post": {
        "description": "Validate a workflow configuration without creating it.",
        "operationId": "validate_workflow_api_v1_workflows_validate_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "title": "Workflow Data",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Validate Workflow Api V1 Workflows Validate Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow breaks a rule (bad_request): the message names it, and names the stage or the field"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Validate Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}": {
      "delete": {
        "description": "Delete a workflow from spot.yaml, one left out at load included.\n\nAnother becomes the default when it was the default. One a route or the\nshadow list names stays: the routing is changed first.",
        "operationId": "delete_workflow_api_v1_workflows__workflow_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Workflow Api V1 Workflows  Workflow Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A route or the shadow list names the workflow (the message names which), or it is the last one: one always stays"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Delete Workflow",
        "tags": [
          "Workflow Management"
        ]
      },
      "get": {
        "description": "Get detailed information about a specific workflow.",
        "operationId": "get_workflow_api_v1_workflows__workflow_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Workflow Api V1 Workflows  Workflow Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow was left out at load: the message gives the rule it breaks"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator answers 500, or the call to it fails otherwise; the message says what"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator refused the gateway's internal key"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator cannot be reached"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator does not answer in time"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow",
        "tags": [
          "Workflow Management"
        ]
      },
      "head": {
        "description": "Get detailed information about a specific workflow.",
        "operationId": "get_workflow_api_v1_workflows__workflow_id__head",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Workflow Api V1 Workflows  Workflow Id  Head",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow",
        "tags": [
          "Workflow Management"
        ]
      },
      "put": {
        "description": "Update an existing workflow: validated, then saved into spot.yaml.\n\nIt replaces a workflow left out at load too, so one can be fixed through\nthe API. The gateway numbers its version, 1 + the highest version the\nworkflow has had (in force or in its history), and keeps the stored\nauthor: the body's ``version`` and ``created_by`` are dropped, whatever\nthey hold.",
        "operationId": "update_workflow_api_v1_workflows__workflow_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "title": "Workflow Data",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Update Workflow Api V1 Workflows  Workflow Id  Put",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow breaks a rule (bad_request): the message names it, and names the stage or the field; or its id is not the one in the path"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery); nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Update Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/clone": {
      "post": {
        "description": "Copy a workflow under a new id: saved into spot.yaml, answered as a create.\n\nThe copy's version is numbered as a create numbers it, and its author is\nthe user.",
        "operationId": "clone_workflow_api_v1_workflows__workflow_id__clone_post",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowCloneRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Clone Workflow Api V1 Workflows  Workflow Id  Clone Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow that loads has this id"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "A workflow has the new id already, one left out at load included"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery); nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Clone Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/history": {
      "get": {
        "description": "A workflow's versions, newest first: each commit that changed it, deleted it included.",
        "operationId": "get_workflow_history_api_v1_workflows__workflow_id__history_get",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum versions to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "description": "Maximum versions to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of versions to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of versions to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowHistoryPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow History",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/history/{commit_id}": {
      "get": {
        "description": "A workflow as a commit stores it, and its diff from the commit before.",
        "operationId": "get_workflow_version_api_v1_workflows__workflow_id__history__commit_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          },
          {
            "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
            "in": "path",
            "name": "commit_id",
            "required": true,
            "schema": {
              "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
              "pattern": "^[0-9a-f]{7,40}$",
              "title": "Commit Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowAtCommit"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No commit of the history has this id, or more than one starts with it"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery)"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow Version",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/history/{commit_id}/restore": {
      "post": {
        "description": "Save a workflow as a commit stores it, as its next version.\n\nA workflow of contract 1 is converted first; one that breaks a rule is\nrefused. The restore is a change of its own, a commit by the user;\na deleted workflow comes back the same way.",
        "operationId": "restore_workflow_version_api_v1_workflows__workflow_id__history__commit_id__restore_post",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          },
          {
            "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
            "in": "path",
            "name": "commit_id",
            "required": true,
            "schema": {
              "description": "A commit's id, or a prefix of 7 or more hex digits only it starts with",
              "pattern": "^[0-9a-f]{7,40}$",
              "title": "Commit Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Restore Workflow Version Api V1 Workflows  Workflow Id  History  Commit Id  Restore Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow, as that commit stores it, breaks a rule: the message names it"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No commit of the history has this id, or more than one starts with it; or that commit holds no workflow of this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml cannot be written: nothing is saved"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "spot.yaml's history (history.git, beside it) cannot be read: an object or a ref of it is missing or damaged; move it aside (the configuration reference's recovery); nothing is saved"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Restore Workflow Version",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/set-default": {
      "post": {
        "description": "Mark a workflow as the platform default.\n\nThe default workflow is the one used when an analysis is submitted\nwithout an explicit ``workflow_id`` (most retrievers fall in this\ncategory). Persists to spot.yaml; effective on the next request\n(api-gateway reads the default per-request).",
        "operationId": "set_default_workflow_api_v1_workflows__workflow_id__set_default_post",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Set Default Workflow Api V1 Workflows  Workflow Id  Set Default Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow has this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Set Default Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/shadow": {
      "get": {
        "description": "Where the workflow, run in the shadow of production, decided another verdict class.\n\nEach difference names the job, production's verdict and score, and the\nworkflow's; newest first. Viewers read them: they hold no content. A\nworkflow that never ran in the shadow, or never differed, has none.\n\n``compared`` and ``differed`` are the rate: the production results the\nworkflow ran on in the last ``days`` UTC days, today's included, and those\non which it differed. An email analysed again counts again.",
        "operationId": "get_workflow_shadow_api_v1_workflows__workflow_id__shadow_get",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum differences to return",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "description": "Maximum differences to return",
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Number of differences to skip",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "description": "Number of differences to skip",
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            }
          },
          {
            "description": "UTC days the rate of differences covers, today's included",
            "in": "query",
            "name": "days",
            "required": false,
            "schema": {
              "default": 30,
              "description": "UTC days the rate of differences covers, today's included",
              "maximum": 90,
              "minimum": 1,
              "title": "Days",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShadowDifferencePage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The database is not available"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Workflow Shadow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/api/v1/workflows/{workflow_id}/test": {
      "post": {
        "description": "Dry-run a saved workflow on a message.\n\nThe body is the message, as at ``POST /api/v1/analyze``: a\n``message/rfc822`` body, or JSON with the message in base64. It is\nparsed as at intake, within the same limits. When the gateway already\ntakes as many messages as it can, the answer is 503, to retry later.\n\nThe message is stored, encrypted, for the length of the run, as an\nemail no list shows, and deleted when the run ends: analyzers fetch its\nraw copy and its parts (``/internal/emails/{email_id}/raw`` and\n``/parts/{part_id}``) as during an analysis. Nothing else is stored: no\njob, no analysis, no statistics.\n\nThe orchestrator answers once the run is over, so the gateway waits\nfor the workflow's own timeout plus a margin, or for the longest\ntimeout a workflow may declare when it does not know the workflow.",
        "operationId": "test_workflow_api_v1_workflows__workflow_id__test_post",
        "parameters": [
          {
            "in": "path",
            "name": "workflow_id",
            "required": true,
            "schema": {
              "title": "Workflow Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RawMessage"
              }
            },
            "message/rfc822": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Test Workflow Api V1 Workflows  Workflow Id  Test Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No workflow has this id"
          },
          "408": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message did not arrive within intake.body_timeout_s"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The workflow was left out at load: the message gives the rule it breaks"
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message is larger than 25 MiB (34 MiB as JSON)"
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message is neither message/rfc822 nor JSON with the message in base64"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message does not validate, is empty or could not be parsed within a limit (parse_limit), or the orchestrator refuses its view"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The run failed, or the analyzer-orchestrator fails otherwise; the message says what"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator refused the gateway's internal key"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator cannot be reached or has no plugin tokens, or the gateway already takes as many messages as it can: retry later"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The analyzer-orchestrator does not answer in time"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Test Workflow",
        "tags": [
          "Workflow Management"
        ]
      }
    },
    "/auth/client-credentials": {
      "post": {
        "description": "OAuth2 client credentials flow.\n\nArgs:\n    client_id: OAuth client identifier\n    client_secret: OAuth client secret\n    auth_service: Authentication service (injected)\n\nReturns:\n    Token for client credentials\n\nRaises:\n    HTTPException: 401 if credentials are invalid",
        "operationId": "client_credentials_auth_client_credentials_post",
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Body_client_credentials_auth_client_credentials_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Client Credentials",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/auth/health": {
      "get": {
        "description": "Auth service health check.\n\nReturns:\n    Health status response",
        "operationId": "auth_health_auth_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Auth Health",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/auth/me": {
      "get": {
        "description": "Get current authenticated user profile.\n\nArgs:\n    current_user: Currently authenticated user (injected)\n    auth_service: Authentication service (injected)\n\nReturns:\n    User profile information",
        "operationId": "get_current_user_profile_auth_me_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserProfile"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Current User Profile",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/auth/refresh": {
      "post": {
        "description": "Refresh access token using a valid refresh token.\n\nArgs:\n    request: Refresh request containing the refresh token\n    auth_service: Authentication service (injected)\n\nReturns:\n    New Token with fresh access_token and refresh_token\n\nRaises:\n    HTTPException: 401 if refresh token is invalid or expired",
        "operationId": "refresh_token_auth_refresh_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefreshRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Refresh Token",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/auth/register": {
      "post": {
        "description": "Register new user (admin-only).\n\nNote: This endpoint is deprecated. Use POST /api/v1/users instead.\n\nArgs:\n    user_data: User registration data\n    auth_service: Authentication service (injected)\n    admin_user: Current admin user (for authorization)\n\nReturns:\n    Dictionary with user details and success message\n\nRaises:\n    HTTPException: 400 if username/email already exists\n    HTTPException: 403 if not admin",
        "operationId": "register_auth_register_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Register Auth Register Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Register",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/auth/token": {
      "post": {
        "description": "OAuth2 password flow login endpoint.\n\nArgs:\n    form_data: OAuth2 form with username and password\n    auth_service: Authentication service (injected)\n\nReturns:\n    Token with access_token and refresh_token\n\nRaises:\n    HTTPException: 401 if credentials are invalid",
        "operationId": "login_auth_token_post",
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Body_login_auth_token_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Login",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/health": {
      "get": {
        "description": "Health check endpoint with dependency status.",
        "operationId": "health_check_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "503": {
            "description": "Service degraded"
          }
        },
        "summary": "Health Check",
        "tags": [
          "Health"
        ]
      },
      "head": {
        "description": "Health check endpoint with dependency status.",
        "operationId": "health_check_health_head",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Health Check",
        "tags": [
          "Health"
        ]
      }
    },
    "/internal/emails/{email_id}/parts/{part_id}": {
      "get": {
        "description": "One decoded part of the stored message, with its media type (scope ``content:read``).\n\nParts are numbered as the view's (``Part.part_id``); a container\n(``multipart/*``, ``message/*``) is not served. The message is parsed\nagain, in a parse worker: within the intake's limits (422 ``parse_limit``\npast one); the part counts in the gateway's bound on content served at\nonce until it is sent (503 past it). Nothing of the part is kept. A\ncontent type that is no media type a header can carry is served as\n``application/octet-stream``. Each read is logged, without the content.",
        "operationId": "get_part_internal_emails__email_id__parts__part_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "part_id",
            "required": true,
            "schema": {
              "title": "Part Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "*/*": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The decoded part, with its media type"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email or part, or a container part"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "An email stored before 2.0, which has no raw message or view (conflict)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The message could not be parsed within a limit (parse_limit), or the email id is not a UUID (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can, or no parse worker could take the message (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Get Part",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/internal/emails/{email_id}/raw": {
      "get": {
        "description": "The stored message, as received (scope ``content:read``).\n\nEach read is logged, without the content. The message counts in the\ngateway's bound on content served at once until it is sent (503 past it).",
        "operationId": "get_raw_internal_emails__email_id__raw_get",
        "parameters": [
          {
            "in": "path",
            "name": "email_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "title": "Email Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "message/rfc822": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The message as received"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "Unknown email"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "An email stored before 2.0, which has no raw message or view (conflict)"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The email id is not a UUID (validation_error)"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The gateway serves as many messages as it can (service_unavailable): retry later"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Get Raw",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/internal/ingest": {
      "post": {
        "description": "Parse and store one message for a retriever, then queue it.\n\nThe body is an ``IngestionRequest`` (at most 34 MiB, the message in\nbase64). The email's route chooses its workflow and caps the wait with\nits time limit; when no route takes it, the default workflow runs. The\njob is in enforce only when the retriever's connection and the route\nboth are, and the answer's ``mode`` says which. A message larger than\n25 MiB is refused (413), and so is an empty one or one that cannot be\nparsed within the limits (422): nothing is stored then. When the gateway\nalready takes as many messages as it can, the answer is 503, to retry\nlater. The mail-orchestrator's answer is relayed: an ``IngestionResult``,\nwith the verdict when the retriever waits for it. When it cannot queue\nthe message (503) or the analysis fails (502), its reason comes in the\ngateway's error envelope.",
        "operationId": "ingest_internal_ingest_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IngestionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestionResult"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Ingest",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/internal/jobs/{job_id}": {
      "get": {
        "description": "An ingestion job, visible only to the retriever that submitted it.",
        "operationId": "get_job_internal_jobs__job_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Get Job",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/internal/jobs/{job_id}/action": {
      "post": {
        "description": "What a retriever did with one of its jobs' messages, or would have done in observe.\n\nScope ``ingest``. The job is the retriever's own, as ``GET\n/internal/jobs/{job_id}`` finds it, and the action is recorded in its\nmode: in observe, nothing may be applied (422). One action per job and\nretriever: a second report replaces the first. Another error of the\nmail-orchestrator comes in the gateway's error envelope.",
        "operationId": "report_action_internal_jobs__job_id__action_post",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActionReport"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The retriever has no job of this id"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The body is no action report, or it says applied for a job in observe"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The mail orchestrator or the database is not available: retry later"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Report Action",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/internal/results/{job_id}": {
      "get": {
        "description": "A workflow's result, as ``SpotClient.result`` reads it (scope ``results``).\n\nA job still queued or running has no result yet: 409, which the SDK's\nclient raises as a ``SpotAPIError`` a responder can tell from a 404 and\nretry. A job that is unknown, failed or analysed before 2.0 has none: 404.",
        "operationId": "get_result_internal_results__job_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "title": "Job Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrchestrationResult"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No result: an unknown job, a failed one or one analysed before 2.0"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The job is not finished: retry later"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "PluginToken": []
          }
        ],
        "summary": "Get Result",
        "tags": [
          "Plugin API"
        ]
      }
    },
    "/metrics": {
      "get": {
        "operationId": "metrics_metrics_get",
        "responses": {
          "200": {
            "content": {
              "text/plain": {}
            },
            "description": "Prometheus text format"
          },
          "401": {
            "description": "Without the metrics token"
          },
          "503": {
            "description": "Metrics are off: METRICS_TOKEN is not set"
          }
        },
        "security": [
          {
            "MetricsToken": []
          }
        ],
        "summary": "Metrics",
        "tags": [
          "Metrics"
        ]
      }
    },
    "/version": {
      "get": {
        "description": "Version information endpoint.",
        "operationId": "version_info_version_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Version Info Version Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Version Info",
        "tags": [
          "Health"
        ]
      }
    }
  }
}
