{
  "components": {
    "schemas": {
      "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"
      },
      "AnalyzeRequest": {
        "description": "The body of ``POST {plugin}/internal/analyze``; the answer is an ``AnalyzerVerdict``.\n\nSerialise it with ``by_alias=True``, so the email's ``from`` survives.",
        "properties": {
          "deadline_ms": {
            "description": "What is left of the analyzer's timeout",
            "title": "Deadline Ms",
            "type": "integer"
          },
          "email": {
            "$ref": "#/components/schemas/EmailView"
          },
          "job_id": {
            "title": "Job Id",
            "type": "string"
          },
          "previous": {
            "additionalProperties": {
              "$ref": "#/components/schemas/StageOutcome"
            },
            "description": "Earlier stages, by name",
            "title": "Previous",
            "type": "object"
          },
          "retrieval_limits": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RetrievalLimits"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          }
        },
        "required": [
          "job_id",
          "email",
          "deadline_ms"
        ],
        "title": "AnalyzeRequest",
        "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"
              }
            ],
            "default": 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"
      },
      "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"
              }
            ],
            "default": null,
            "description": "The values concerned, as JSON strings: at most 200 bytes of JSON",
            "title": "Detail"
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": 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"
      },
      "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"
      },
      "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"
      },
      "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"
              }
            ],
            "default": null,
            "title": "Date"
          },
          "envelope": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Envelope"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "from": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "type": "null"
              }
            ],
            "default": 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"
              }
            ],
            "default": 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"
              }
            ],
            "default": null,
            "title": "In Reply To"
          },
          "message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": 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"
              }
            ],
            "default": 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"
              }
            ],
            "default": null,
            "title": "Client Address"
          },
          "client_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Client Name"
          },
          "helo": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Helo"
          },
          "mail_from": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Mail From"
          },
          "rcpt_to": {
            "items": {
              "type": "string"
            },
            "title": "Rcpt To",
            "type": "array"
          }
        },
        "title": "Envelope",
        "type": "object"
      },
      "ErrorAnswer": {
        "description": "An error the app answers itself: 401 without the plugin's own token, 500\nwhen the handler fails. It never holds the request's content.",
        "properties": {
          "detail": {
            "description": "\"unauthorized\", \"analysis failed\", \"response failed\"",
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorAnswer",
        "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"
      },
      "HealthAnswer": {
        "description": "``GET /health``'s body: the plugin's state and the version of its\nmanifest. ``reason`` is left out when there is none.",
        "properties": {
          "reason": {
            "anyOf": [
              {
                "maxLength": 200,
                "pattern": "^[^\\x00-\\x1f\\x7f]*$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why, in a few words; no content, no control character",
            "title": "Reason"
          },
          "status": {
            "enum": [
              "healthy",
              "starting",
              "unhealthy"
            ],
            "title": "Status",
            "type": "string"
          },
          "version": {
            "description": "The manifest's version",
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "status",
          "version"
        ],
        "title": "HealthAnswer",
        "type": "object"
      },
      "InvalidRequestAnswer": {
        "description": "The 422 for a request body that does not validate: the fields refused.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/RefusedField"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "InvalidRequestAnswer",
        "type": "object"
      },
      "Manifest": {
        "description": "A plugin's declaration of what it is, what it needs and what leaves SPOT.",
        "properties": {
          "capabilities": {
            "items": {
              "type": "string"
            },
            "title": "Capabilities",
            "type": "array"
          },
          "compatibility": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SPOT version range, e.g. >=2.0.0,<3.0.0",
            "title": "Compatibility"
          },
          "contract": {
            "default": 2,
            "title": "Contract",
            "type": "integer"
          },
          "egress": {
            "$ref": "#/components/schemas/Egress",
            "default": "content"
          },
          "healthcheck": {
            "default": "/health",
            "title": "Healthcheck",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/PluginKind"
          },
          "port": {
            "default": 8000,
            "title": "Port",
            "type": "integer"
          },
          "requires_external_ingress": {
            "default": false,
            "title": "Requires External Ingress",
            "type": "boolean"
          },
          "resources": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Resources"
              },
              {
                "type": "null"
              }
            ]
          },
          "settings": {
            "additionalProperties": {
              "$ref": "#/components/schemas/SettingField"
            },
            "title": "Settings",
            "type": "object"
          },
          "version": {
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "id",
          "version",
          "kind"
        ],
        "title": "Manifest",
        "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"
      },
      "PluginKind": {
        "description": "What a plugin does; a plugin has exactly one kind.",
        "enum": [
          "analyzer",
          "context_provider",
          "mail_retriever",
          "responder"
        ],
        "title": "PluginKind",
        "type": "string"
      },
      "RefusedField": {
        "description": "A field of a request body that does not validate: never the value sent.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Loc",
            "type": "array"
          },
          "msg": {
            "title": "Msg",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "loc",
          "msg"
        ],
        "title": "RefusedField",
        "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"
      },
      "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,
                "minimum": 1,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": 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"
              }
            ],
            "default": null,
            "description": "Raise the min_score floor even if analyzer asked for less",
            "title": "Min Score Floor"
          }
        },
        "title": "RetrievalLimits",
        "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"
      },
      "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"
      },
      "StageOutcome": {
        "description": "A stage's own outcome, used by stage conditions.",
        "properties": {
          "analyzers": {
            "items": {
              "$ref": "#/components/schemas/AnalyzerOutcome"
            },
            "title": "Analyzers",
            "type": "array"
          },
          "score": {
            "anyOf": [
              {
                "maximum": 100,
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "None when every analyzer of the stage is unknown",
            "title": "Score"
          },
          "stage": {
            "title": "Stage",
            "type": "string"
          },
          "verdict": {
            "$ref": "#/components/schemas/VerdictClass",
            "description": "The policy applied to this stage's analyzers alone"
          }
        },
        "required": [
          "stage",
          "verdict",
          "score",
          "analyzers"
        ],
        "title": "StageOutcome",
        "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"
      },
      "VerdictClass": {
        "description": "The class of an email, for one analyzer or for the workflow.",
        "enum": [
          "malicious",
          "suspicious",
          "spam",
          "benign",
          "unknown"
        ],
        "title": "VerdictClass",
        "type": "string"
      }
    },
    "securitySchemes": {
      "HTTPBearer": {
        "description": "The plugin's own token",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "The routes a plugin of kind analyzer serves, as spot-sdk-python 2.1.1's create_plugin_app declares them.",
    "title": "SPOT plugin: analyzer",
    "version": "2.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/health": {
      "get": {
        "operationId": "health_route_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthAnswer"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Health Route"
      }
    },
    "/internal/analyze": {
      "post": {
        "operationId": "analyze_route_internal_analyze_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzerVerdict"
                }
              }
            },
            "description": "The verdict"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorAnswer"
                }
              }
            },
            "description": "Without the plugin's own token"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvalidRequestAnswer"
                }
              }
            },
            "description": "The body does not validate"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorAnswer"
                }
              }
            },
            "description": "The handler failed"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Analyze Route"
      }
    },
    "/manifest": {
      "get": {
        "operationId": "get_manifest_manifest_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Manifest"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Get Manifest"
      }
    }
  }
}
