{
  "openapi": "3.2.0",
  "info": {
    "title": "mlab.sh API",
    "version": "1.0.0",
    "description": "The **mlab.sh** REST API lets you launch domain security scans, look up IP, crypto, hash, URL, email, phone and MAC intelligence, extract IOCs from raw text, analyze files and scripts, triage indicator lists, run RedKit audits, schedule scans, work on cases and inspect SSL certificates programmatically.\n\nAll endpoints are authenticated with an API key sent in the `Authorization` header. Generate a key from **Organization → Settings → API keys** on [mlab.sh](https://mlab.sh). A key belongs to the organization and carries permissions chosen when it is created (`scan.lookup`, `scan.domain`, `scan.file`, `scan.batch`, `redkit`, `watchdog.read`, `watchdog.write`, `cases.read`, `cases.write`, `tags.read`, `tags.write`, or full access). A call outside the key's permissions returns **403** naming the missing permission; organization and account settings are never reachable with a key.\n\n**Working through an AI agent?** Prefer the Model Context Protocol server at `https://mlab.sh/mcp` (Streamable HTTP, OAuth 2.0 or an `mcp_` token). It exposes 25 tools covering everything below plus CVE search, threat-actor attribution, SBOM scanning and offline indicator lookups that consume no scan quota, so it is a wider surface than this REST API rather than a wrapper over it. See https://doc.mlab.sh/docs/mlab.sh/integrations/mcp and https://mlab.sh/llms.txt.\n\n\n**Permissions.** Each operation lists the API key permission it needs (`x-api-key-permission`). Keys created before 2026-10-01 were migrated to full access.\n",
    "contact": {
      "name": "mlab.sh support",
      "email": "contact@cyberdream.io",
      "url": "https://mlab.sh"
    },
    "license": {
      "name": "Proprietary"
    },
    "x-source-of-truth": "html/openapi.json in the mlab.sh app repository, served at https://mlab.sh/openapi.json. The docs repository generates openapi.yaml from it with scripts/sync-openapi.mjs."
  },
  "servers": [
    {
      "url": "https://mlab.sh",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "General",
      "description": "Service root and authentication check."
    },
    {
      "name": "Domain Scan",
      "description": "Launch, monitor and retrieve automated domain security scans."
    },
    {
      "name": "IP Lookup",
      "description": "Threat intelligence and geolocation for IP addresses."
    },
    {
      "name": "Crypto",
      "description": "Threat intelligence for blockchain addresses."
    },
    {
      "name": "File Scan",
      "description": "Upload files and retrieve static analysis results."
    },
    {
      "name": "SSL",
      "description": "SSL/TLS certificate history for a domain."
    },
    {
      "name": "Hash Lookup",
      "description": "Reputation of a file hash across known-good and known-bad reference sets."
    },
    {
      "name": "URL Analysis",
      "description": "Structural analysis of a URL, with optional redirect resolution."
    },
    {
      "name": "Email Analysis",
      "description": "Mailbox type, spoofability and risk score for an email address."
    },
    {
      "name": "Phone Analysis",
      "description": "Numbering-plan analysis of a phone number."
    },
    {
      "name": "MAC Lookup",
      "description": "Vendor and structure of a MAC address."
    },
    {
      "name": "IOC Extraction",
      "description": "Pull indicators out of raw text, with optional SMS threat scoring."
    },
    {
      "name": "Limits",
      "description": "Inspect your remaining scan quota."
    },
    {
      "name": "Batch Triage",
      "description": "Triage a pasted list of indicators in one call."
    },
    {
      "name": "Script Analysis",
      "description": "AI explanation of a submitted shell script."
    },
    {
      "name": "RedKit",
      "description": "Launch and follow RedKit web audits."
    },
    {
      "name": "Scheduled Scans",
      "description": "Recurring domain scans and RedKit audits."
    },
    {
      "name": "Cases",
      "description": "The organization's shared investigation cases."
    },
    {
      "name": "Finding Tags",
      "description": "Tags on RedKit findings (false positive, accepted risk, fixed)."
    }
  ],
  "paths": {
    "/api/v1/": {
      "get": {
        "tags": [
          "General"
        ],
        "summary": "Authentication check",
        "operationId": "getRoot",
        "description": "Returns a greeting with your organization and plan. Use it to verify that an API key is valid.\n",
        "responses": {
          "200": {
            "description": "Key is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Greeting"
                },
                "example": {
                  "auth": "api_key",
                  "message": "Hello, Acme Corp!",
                  "organization": "Acme Corp",
                  "plan": "pro"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/scan/domain": {
      "post": {
        "tags": [
          "Domain Scan"
        ],
        "summary": "Launch a domain scan",
        "operationId": "launchDomainScan",
        "description": "Starts an asynchronous security scan for a domain. Poll `/api/v1/scan/domain/status` and then fetch `/api/v1/scan/domain/results`.\n\n**API key permission:** `scan.domain`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The domain to scan.",
                    "example": "example.com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scan accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanStarted"
                },
                "example": {
                  "status": "success",
                  "message": "Domain scan has been started."
                }
              }
            }
          },
          "400": {
            "description": "Invalid domain or scan limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidDomain": {
                    "value": {
                      "status": "error",
                      "message": "Provided domain is invalid."
                    }
                  },
                  "limitReached": {
                    "value": {
                      "status": "error",
                      "message": "Scan limit reached. Please try again later."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.domain"
      }
    },
    "/api/v1/scan/domain/status": {
      "get": {
        "tags": [
          "Domain Scan"
        ],
        "summary": "Check scan status",
        "operationId": "getDomainScanStatus",
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Current scan status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanStatus"
                },
                "examples": {
                  "pending": {
                    "value": {
                      "status": "pending",
                      "message": "Domain scan is still pending."
                    }
                  },
                  "scanning": {
                    "value": {
                      "status": "scanning",
                      "message": "Domain scan is still in progress."
                    }
                  },
                  "success": {
                    "value": {
                      "status": "success",
                      "message": "Domain scan is in done."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.domain",
        "description": "\n\n**API key permission:** `scan.domain`.\n"
      }
    },
    "/api/v1/scan/domain/results": {
      "get": {
        "tags": [
          "Domain Scan"
        ],
        "summary": "Get scan results",
        "operationId": "getDomainScanResults",
        "description": "Returns the result set of the last scan: discovered subdomains, DNS resolve and TXT records, SSL certificates and well-known files.\n\nThis payload is frozen and will not change. The full scan (mail security, TLS, HTTP headers, technologies and CVEs, takeover, registration, findings and score) is served by `/api/v1/scan/domain/report`.\n\n**API key permission:** `scan.domain`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Scan results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainResults"
                },
                "example": {
                  "domain": "example.com",
                  "scan_date": "2026-07-05 16:13:00 UTC",
                  "status": "completed",
                  "results": {
                    "subdomains": [
                      "www.example.com",
                      "example.org",
                      "example.net"
                    ],
                    "subdomains_suspicious": [
                      {
                        "keyword": "admin",
                        "subdomain": "admin.example.com"
                      }
                    ],
                    "dns": {
                      "resolve": [
                        {
                          "domain": "www.example.com",
                          "a": [
                            "104.20.23.154",
                            "172.66.147.243"
                          ],
                          "aaaa": [
                            "2606:4700:10::6814:179a"
                          ],
                          "cname": null
                        }
                      ],
                      "txt": {
                        "raw": [
                          "v=spf1 -all"
                        ],
                        "spf": "v=spf1 -all",
                        "dmarc": null,
                        "dkim": []
                      }
                    },
                    "ssl": [
                      {
                        "domain": "example.com",
                        "id": 13769145126,
                        "issuer_ca_id": 0,
                        "issuer_name": "C=US, O=DigiCert Inc, CN=DigiCert Global CA G2",
                        "common_name": "www.example.com",
                        "name_value": "www.example.com",
                        "entry_timestamp": "2026-02-11T21:22:07Z",
                        "not_before": "2026-01-15T00:00:00",
                        "not_after": "2027-01-15T23:59:59",
                        "serial_number": "",
                        "result_count": 1
                      }
                    ],
                    "files": {
                      "robots_txt": "User-agent: *\nDisallow:",
                      "security_txt": ""
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.domain"
      }
    },
    "/api/v1/scan/domain/report": {
      "get": {
        "tags": [
          "Domain Scan"
        ],
        "summary": "Get the full scan report",
        "operationId": "getDomainScanReport",
        "description": "The complete domain scan, schema `domain-report/2`: every check grouped by section (registration, dns, mail, web, certificates, surface), the aggregated findings with the section they belong to, and the score shown on mlab.sh.\n\n**Stability.** Fields are only ever added to this schema; a breaking change ships as a new schema string on a new route. Clients should ignore fields they do not know.\n\n`status` is `completed`, `partial`, `failed` or `in_progress`; `score` is `null` while the scan is in progress. `dns.caa` is `null` when the scan predates the CAA audit. `surface.hosts` lists every live host; per-host HTTP, TLS and technology data will be added to each entry.\n\n**API key permission:** `scan.domain`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Full scan report (example abridged).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "schema": "domain-report/2",
                  "domain": "example.com",
                  "status": "completed",
                  "scan_date": "2026-10-09 10:00:00 UTC",
                  "score": {
                    "value": 72,
                    "grade": "C"
                  },
                  "findings": [
                    {
                      "severity": "high",
                      "title": "Subdomain takeover: old.example.com points to an unclaimed Microsoft Azure resource",
                      "section": "surface"
                    },
                    {
                      "severity": "medium",
                      "title": "DMARC policy is p=none: spoofed mail is reported but still delivered",
                      "section": "email"
                    }
                  ],
                  "registration": {
                    "registrar": "Example Registrar",
                    "created": "1995-08-14T04:00:00Z",
                    "expires": "2027-08-13T04:00:00Z",
                    "expires_in_days": 308,
                    "locks": {
                      "transfer": true,
                      "delete": true,
                      "update": true,
                      "renew": false
                    },
                    "findings": []
                  },
                  "dns": {
                    "records": {
                      "mx": [
                        {
                          "priority": 10,
                          "exchange": "mx1.example.com"
                        }
                      ],
                      "ns": [
                        "a.iana-servers.net"
                      ],
                      "caa": [],
                      "soa": null,
                      "ptr": []
                    },
                    "dnssec": {
                      "enabled": true,
                      "status": "secure",
                      "ds_algorithms": [
                        13
                      ],
                      "findings": []
                    },
                    "caa": null,
                    "txt": {
                      "raw": [
                        "v=spf1 include:_spf.google.com -all"
                      ],
                      "spf": "v=spf1 include:_spf.google.com -all",
                      "dmarc": "v=DMARC1; p=none",
                      "dkim": [
                        "google"
                      ],
                      "saas": [
                        {
                          "service": "Google Workspace",
                          "category": "email",
                          "source": "spf",
                          "evidence": "_spf.google.com"
                        }
                      ]
                    },
                    "dangling": {
                      "checked": 3,
                      "dangling_count": 0,
                      "findings": []
                    }
                  },
                  "mail": {
                    "spf": {
                      "present": true,
                      "all": "-all",
                      "lookups": 4,
                      "void_lookups": 0,
                      "includes": [
                        "_spf.google.com"
                      ],
                      "permerror": false
                    },
                    "dmarc": {
                      "present": true,
                      "policy": "none",
                      "effective_policy": "none",
                      "subdomain_policy": "none"
                    },
                    "grade": "C",
                    "findings": []
                  },
                  "web": {
                    "tls": {
                      "protocol": "TLSv1.3",
                      "days_remaining": 98
                    },
                    "http": {
                      "hsts": {
                        "present": true
                      }
                    },
                    "cve": {
                      "findings": []
                    },
                    "wellknown": {},
                    "files": {
                      "security_txt": "",
                      "robots_txt": "User-agent: *"
                    }
                  },
                  "certificates": {
                    "history": {
                      "cert_count": 42,
                      "duplicates_removed": 42,
                      "wildcards": [
                        "*.example.com"
                      ],
                      "valid_cas": [
                        "DigiCert"
                      ]
                    },
                    "neighbors": {},
                    "list": []
                  },
                  "surface": {
                    "subdomains": [
                      "www.example.com"
                    ],
                    "suspicious": [],
                    "ghosts": {},
                    "hosts": [
                      {
                        "domain": "www.example.com",
                        "a": [
                          "104.20.23.154"
                        ],
                        "aaaa": [],
                        "cname": null
                      }
                    ],
                    "takeover": {
                      "candidates": [],
                      "findings": []
                    },
                    "ip_intel": {
                      "ips": [
                        {
                          "ip": "104.20.23.154",
                          "as": "AS13335 Cloudflare, Inc.",
                          "country_code": "US",
                          "category": "cdn",
                          "provider": "Cloudflare"
                        }
                      ],
                      "provider_summary": [
                        {
                          "category": "cdn",
                          "provider": "Cloudflare",
                          "count": 1
                        }
                      ],
                      "reserved": [],
                      "findings": []
                    },
                    "typosquat": {}
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.domain"
      }
    },
    "/api/v1/scan/domain/loadnetworkrequest": {
      "get": {
        "tags": [
          "Domain Scan"
        ],
        "summary": "Capture network requests",
        "operationId": "loadNetworkRequests",
        "description": "Loads the target URL in a headless browser and returns every network request the page makes (documents, scripts, XHR, images…) with method, status, MIME type, bytes, timing and remote IP. Useful for auditing trackers, third-party calls and supply-chain footprint.\n\n**Quota:** counts as 1 domain scan. **Latency:** ~10–30s (real browser capture).\n\n**API key permission:** `scan.domain`.\n",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Target URL to load. `https://` is prepended if no scheme is present. Must be http or https.\n",
            "schema": {
              "type": "string",
              "example": "https://example.com"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Captured network activity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NetworkCapture"
                },
                "example": {
                  "url": "https://example.com/",
                  "captureMs": 10000,
                  "count": 1,
                  "totalBytes": 632,
                  "requests": [
                    {
                      "requestId": "E51577303D719753FF104B65E699F860",
                      "url": "https://example.com/",
                      "method": "GET",
                      "resourceType": "Document",
                      "initiator": "other",
                      "status": 200,
                      "mimeType": "text/html",
                      "protocol": "http/1.1",
                      "remoteIP": "93.184.216.34",
                      "encodedBytes": 632,
                      "startMs": 0,
                      "endMs": 77,
                      "failed": false,
                      "errorText": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.domain"
      }
    },
    "/api/v1/scan/ip": {
      "get": {
        "tags": [
          "IP Lookup"
        ],
        "summary": "Look up an IP address or CIDR range",
        "operationId": "lookupIp",
        "description": "For a single address: geolocation, ISP/ASN, network type (proxy, hosting, mobile), reverse DNS with forward confirmation, the RDAP allocation record for its block including the network abuse contact and Tor exit-node status. Address lookups are cached for 7 days and consume IP scan quota.\n\nFor a CIDR range such as `10.0.0.0/8`: network and broadcast address, the usable host window, netmask and wildcard, range size, and the reserved block it falls in, if any. Range analysis is arithmetic on the address bits, so it makes no upstream call and consumes no quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "ip",
            "in": "query",
            "required": true,
            "description": "IPv4 or IPv6 address, or a CIDR range, to look up.",
            "schema": {
              "type": "string",
              "example": "8.8.8.8"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "IP intelligence. The shape differs for a reserved address and for a CIDR range.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IpInfo"
                    },
                    {
                      "$ref": "#/components/schemas/ReservedIp"
                    },
                    {
                      "$ref": "#/components/schemas/CidrRange"
                    }
                  ]
                },
                "examples": {
                  "public": {
                    "summary": "Public IP (8.8.8.8)",
                    "value": {
                      "ip": "8.8.8.8",
                      "reserved": false,
                      "isp": "Google LLC",
                      "org": "Google Public DNS",
                      "as": "AS15169 Google LLC",
                      "city": "Ashburn",
                      "region": "Virginia",
                      "country": "United States",
                      "country_code": "US",
                      "continent": "North America",
                      "continent_code": "NA",
                      "timezone": "America/New_York",
                      "zip": "20149",
                      "lat": 39.03,
                      "lon": -77.5,
                      "currency": "USD",
                      "proxy": false,
                      "hosting": true,
                      "mobile": false,
                      "rdns": {
                        "ip": "8.8.8.8",
                        "found": true,
                        "names": [
                          "dns.google"
                        ],
                        "name": "dns.google",
                        "forward_confirmed": true,
                        "forward_addresses": [
                          "8.8.8.8"
                        ]
                      },
                      "rdap": {
                        "ip": "8.8.8.8",
                        "found": true,
                        "name": "GOGL",
                        "cidr": "8.8.8.0/24",
                        "country": "US",
                        "holder": "Google LLC",
                        "abuse_email": "network-abuse@google.com"
                      },
                      "tor": {
                        "available": true,
                        "ip": "8.8.8.8",
                        "is_tor": false
                      },
                      "status": "success"
                    }
                  },
                  "reserved": {
                    "summary": "Reserved/private IP (192.168.1.1)",
                    "value": {
                      "ip": "192.168.1.1",
                      "reserved": true,
                      "type": "ipv4",
                      "range": "Private-use (192.168.0.0/16)",
                      "rfc": "RFC 1918"
                    }
                  },
                  "cidr": {
                    "summary": "CIDR range (10.0.0.0/8)",
                    "value": {
                      "cidr": "10.0.0.0/8",
                      "input": "10.0.0.0/8",
                      "input_is_network": true,
                      "version": "ipv4",
                      "prefix_length": 8,
                      "host_bits": 24,
                      "network": "10.0.0.0",
                      "broadcast": "10.255.255.255",
                      "first_usable": "10.0.0.1",
                      "last_usable": "10.255.255.254",
                      "netmask": "255.0.0.0",
                      "wildcard": "0.255.255.255",
                      "total_addresses": "16777216",
                      "usable_hosts": "16777214",
                      "reserved": {
                        "range": "Private-use (10.0.0.0/8)",
                        "rfc": "RFC 1918"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/crypto": {
      "get": {
        "tags": [
          "Crypto"
        ],
        "summary": "Look up a blockchain address",
        "operationId": "lookupCrypto",
        "description": "Labels, sanctions status, risk scoring and address type for an address on Bitcoin, any of 13 EVM chains, Tron, Solana, TON or Dogecoin.\n\nThe address family is identified by decoding the address, not by pattern matching, and its checksum is verified on the way: `address_info.checksum` of `invalid` means the address is malformed, which is almost always a typo.\n\n**Chain selection.** All 13 EVM chains share one address format, so for an EVM address the chain cannot be derived from the address. `chain_source` says where the chain came from: `detected` (read out of the address), `explicit` (you passed it) or `default` (nothing could decide, so ETH was assumed). When `chain_ambiguous` is true, pass `chain` yourself: upstream labels are filtered by chain, so querying the wrong one returns an empty-looking result rather than an error.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "required": true,
            "description": "The blockchain address to look up.",
            "schema": {
              "type": "string",
              "example": "0x722122df12d4e14e13ac3b6895a86e84145b6967"
            }
          },
          {
            "name": "chain",
            "in": "query",
            "required": false,
            "description": "Chain ID. Auto-detected for EVM (`0x…`), BTC (`1…/3…/bc1…`) and TRX (`T…`) addresses.\n",
            "schema": {
              "type": "string",
              "enum": [
                "ETH",
                "BSC",
                "POLYGON",
                "ARBITRUM",
                "OPTIMISM",
                "BASE",
                "AVAX",
                "BTC",
                "TRX",
                "SOL",
                "TON",
                "DOGE"
              ],
              "example": "ETH"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Crypto address intelligence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CryptoAddress"
                },
                "example": {
                  "address": "0x722122df12d4e14e13ac3b6895a86e84145b6967",
                  "chain": "ETH",
                  "chain_source": "default",
                  "chain_ambiguous": true,
                  "chain_candidates": [
                    "ETH",
                    "BSC",
                    "POLYGON",
                    "ARBITRUM",
                    "OPTIMISM",
                    "BASE",
                    "AVAX",
                    "BLAST",
                    "FLARE",
                    "LINEA",
                    "MANTA",
                    "MANTLE",
                    "SONIC"
                  ],
                  "address_info": {
                    "family": "evm",
                    "kind": "account",
                    "checksum": "absent",
                    "testnet": false
                  },
                  "intel": {
                    "type": "contract",
                    "categories": [
                      "contract",
                      "mixer"
                    ],
                    "labels": [
                      {
                        "name": "Tornado Cash: Proxy",
                        "source": "community"
                      }
                    ],
                    "sanctions": {
                      "is_sanctioned": false
                    },
                    "risk_score": 80,
                    "risk_level": "critical"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "description": "The crypto intelligence upstream could not be reached. Not a verdict: retry."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      },
      "post": {
        "tags": [
          "Crypto"
        ],
        "summary": "Look up a batch of blockchain addresses",
        "operationId": "lookupCryptoBulk",
        "description": "The same lookup as the GET route, batched: up to 100 addresses in one request. The motivating case is a list pulled out of a ransom note, a dump or a report.\n\n`chain` is optional and applies to every address in the batch. It is worth passing for a list of EVM addresses, which are otherwise all defaulted to Ethereum.\n\nThe upstream has no batch endpoint, so each address is its own request and each is metered separately. Requires an authenticated caller.\n\n**API key permission:** `scan.lookup`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "addresses"
                ],
                "properties": {
                  "addresses": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "string"
                    },
                    "description": "Blockchain addresses. Duplicates and blank entries are dropped."
                  },
                  "chain": {
                    "type": "string",
                    "example": "BSC",
                    "description": "Applied to every address in the batch. Omit to derive it per address."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results in request order, plus what could not be done.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CryptoAddress"
                      },
                      "description": "An entry whose upstream call failed carries `error: upstream_unavailable` instead of a verdict."
                    },
                    "invalid": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Entries that could not be read as an address at all."
                    },
                    "deferred": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Entries the daily quota stopped us reaching. Not errors: not done."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Batch lookups require an authenticated caller."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/hash": {
      "get": {
        "tags": [
          "Hash Lookup"
        ],
        "summary": "Look up a file hash",
        "operationId": "lookupHash",
        "description": "Checks an MD5, SHA-1 or SHA-256 digest against CIRCL hashlookup (NIST NSRL), which knows legitimate files and flags some known malware, plus any sample already analysed on mlab (`mlab_sample`). Answers are cached.\n\n`unknown` means the sources were reached and none knows the digest. `unavailable` means a source could not be reached; it is never cached, retry. `sources_answered` says how many feeds actually answered. A SHA-512 is accepted but answers `not_queryable`, since no feed indexes it.\n\nOffline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "hash",
            "in": "query",
            "required": true,
            "description": "MD5, SHA-1, SHA-256 or SHA-512 hex digest.",
            "schema": {
              "type": "string",
              "example": "44d88612fea8a8f36de82e1278abb02f"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Look up a file hash",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HashVerdict"
                },
                "example": {
                  "hash": "44d88612fea8a8f36de82e1278abb02f",
                  "algorithm": "MD5",
                  "verdict": "known_malicious",
                  "found": true,
                  "known_malicious": true,
                  "matched_on": "md5",
                  "enrichment_source": "circl-hashlookup",
                  "trust": 100,
                  "cached": true,
                  "sources_hit": 1,
                  "sources_answered": 1,
                  "sources_queried": 1
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      },
      "post": {
        "tags": [
          "Hash Lookup"
        ],
        "summary": "Look up a batch of file hashes",
        "operationId": "lookupHashBulk",
        "description": "The same lookup as the GET route, for up to 500 digests (an EDR export, say). Cached answers come from one query, the rest fan out to the feeds within a 35 s budget. Results come back in request order. Requires an authenticated caller. Offline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hashes"
                ],
                "properties": {
                  "hashes": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verdicts in request order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "cached": {
                      "type": "integer",
                      "description": "How many answers came from the cache."
                    },
                    "summary": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      },
                      "description": "Count per verdict."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HashVerdict"
                      }
                    },
                    "invalid": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Entries that are not a hex digest of a supported length."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/url": {
      "get": {
        "tags": [
          "URL Analysis"
        ],
        "summary": "Analyse a URL",
        "operationId": "analyzeUrl",
        "description": "Parses the URL and flags phishing shapes: IP hosts, punycode, embedded credentials, shorteners, redirect parameters, encoded payloads. `host_context` adds what a previous mlab domain scan recorded about the host (`scanned: false` when there is none).\n\nThe URL is never fetched unless you pass `resolve=true`, which follows redirects to the final destination and fills `resolution`.\n\nOffline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "The URL to analyse (max 8192 chars). `q` is accepted as an alias.",
            "schema": {
              "type": "string",
              "example": "https://bit.ly/3xYz"
            }
          },
          {
            "name": "resolve",
            "in": "query",
            "required": false,
            "description": "Follow the link to its destination. Off by default.",
            "schema": {
              "type": "string",
              "example": "true",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analyse a URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "scheme": {
                      "type": "string"
                    },
                    "host": {
                      "type": "string"
                    },
                    "host_unicode": {
                      "type": "string"
                    },
                    "host_is_ip": {
                      "type": "boolean"
                    },
                    "port": {
                      "type": "integer",
                      "nullable": true
                    },
                    "path": {
                      "type": "string"
                    },
                    "query": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "fragment": {
                      "type": "string",
                      "nullable": true
                    },
                    "has_userinfo": {
                      "type": "boolean",
                      "description": "Credentials are never echoed back."
                    },
                    "decoded": {
                      "type": "string",
                      "nullable": true
                    },
                    "embedded_url": {
                      "type": "string",
                      "nullable": true
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Finding"
                      }
                    },
                    "host_context": {
                      "type": "object"
                    },
                    "resolution": {
                      "type": "object",
                      "nullable": true,
                      "description": "Only with `resolve=true`."
                    }
                  }
                },
                "example": {
                  "url": "http://paypa1-secure.com/login?next=https://paypal.com",
                  "scheme": "http",
                  "host": "paypa1-secure.com",
                  "host_is_ip": false,
                  "path": "/login",
                  "query": [
                    {
                      "key": "next",
                      "value": "https://paypal.com"
                    }
                  ],
                  "has_userinfo": false,
                  "findings": [
                    {
                      "severity": "high",
                      "title": "Brand lookalike host",
                      "detail": "The host imitates paypal."
                    }
                  ],
                  "host_context": {
                    "scanned": false
                  },
                  "resolution": null
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/email": {
      "get": {
        "tags": [
          "Email Analysis"
        ],
        "summary": "Analyse an email address",
        "operationId": "analyzeEmail",
        "description": "Mailbox type (disposable, role account, consumer provider), canonical form, and how easily the domain can be spoofed, judged from its SPF, DMARC and DKIM. Mail policy comes from the last mlab domain scan when there is one, otherwise from a live DNS read (`domain_scan.mail_source`).\n\n`score.conclusive` is false when there was no published policy to judge: a low score then means little is known, not that the address is fine.\n\nOffline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": true,
            "description": "The email address to analyse.",
            "schema": {
              "type": "string",
              "example": "john.doe+news@gmail.com"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analyse an email address",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "local": {
                      "type": "string"
                    },
                    "domain": {
                      "type": "string"
                    },
                    "domain_unicode": {
                      "type": "string"
                    },
                    "tag": {
                      "type": "string",
                      "nullable": true
                    },
                    "canonical": {
                      "type": "string"
                    },
                    "canonical_differs": {
                      "type": "boolean"
                    },
                    "mailbox_type": {
                      "type": "string",
                      "enum": [
                        "Disposable",
                        "Role account",
                        "Consumer provider",
                        "Standard"
                      ]
                    },
                    "is_role": {
                      "type": "boolean"
                    },
                    "is_free_provider": {
                      "type": "boolean"
                    },
                    "is_disposable": {
                      "type": "boolean"
                    },
                    "score": {
                      "type": "object",
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "band": {
                          "type": "string",
                          "enum": [
                            "clean",
                            "suspect",
                            "high",
                            "critical"
                          ]
                        },
                        "conclusive": {
                          "type": "boolean"
                        },
                        "reasons": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        }
                      }
                    },
                    "domain_scan": {
                      "type": "object",
                      "properties": {
                        "scanned": {
                          "type": "boolean"
                        },
                        "mail_source": {
                          "type": "string",
                          "enum": [
                            "scan",
                            "live",
                            ""
                          ]
                        },
                        "spoofability": {
                          "type": "object",
                          "properties": {
                            "verdict": {
                              "type": "string"
                            },
                            "summary": {
                              "type": "string"
                            }
                          }
                        },
                        "mx": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "count": {
                              "type": "integer"
                            }
                          }
                        },
                        "domain_age_days": {
                          "type": "integer",
                          "nullable": true
                        },
                        "auth": {
                          "type": "object",
                          "nullable": true,
                          "description": "SPF, DMARC and DKIM as published. Null when mail was never collected."
                        }
                      }
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Finding"
                      }
                    }
                  }
                },
                "example": {
                  "email": "john.doe+news@gmail.com",
                  "local": "john.doe+news",
                  "domain": "gmail.com",
                  "tag": "news",
                  "canonical": "johndoe@gmail.com",
                  "canonical_differs": true,
                  "mailbox_type": "Consumer provider",
                  "is_role": false,
                  "is_free_provider": true,
                  "is_disposable": false,
                  "score": {
                    "value": 10,
                    "band": "clean",
                    "conclusive": true,
                    "reasons": []
                  },
                  "domain_scan": {
                    "scanned": true,
                    "mail_source": "scan",
                    "spoofability": {
                      "verdict": "Spoofing rejected",
                      "summary": "DMARC p=reject is enforced."
                    },
                    "mx": {
                      "count": 5
                    },
                    "domain_age_days": 10950
                  },
                  "findings": []
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/phone": {
      "get": {
        "tags": [
          "Phone Analysis"
        ],
        "summary": "Analyse a phone number",
        "operationId": "analyzePhone",
        "description": "Validity, country, area, line type and allocated operator from numbering-plan metadata, plus scam shapes (premium-rate, callback ranges). The number is never dialled. The operator is the one the range was allocated to: when `allocated_operator_may_have_changed` is true the line may have been ported.\n\nOffline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "number",
            "in": "query",
            "required": true,
            "description": "Phone number in E.164 form. A missing leading `+` is added.",
            "schema": {
              "type": "string",
              "example": "+33612345678"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analyse a phone number",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "input": {
                      "type": "string"
                    },
                    "valid": {
                      "type": "boolean"
                    },
                    "possible": {
                      "type": "boolean"
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "Not a real number",
                        "Scam shape",
                        "Worth a look",
                        "Nothing notable"
                      ]
                    },
                    "country_code": {
                      "type": "integer"
                    },
                    "region": {
                      "type": "string",
                      "nullable": true
                    },
                    "area": {
                      "type": "string",
                      "nullable": true
                    },
                    "line_type": {
                      "type": "string"
                    },
                    "line_type_note": {
                      "type": "string"
                    },
                    "allocated_operator": {
                      "type": "string",
                      "nullable": true
                    },
                    "allocated_operator_may_have_changed": {
                      "type": "boolean"
                    },
                    "formats": {
                      "type": "object",
                      "properties": {
                        "e164": {
                          "type": "string"
                        },
                        "international": {
                          "type": "string"
                        },
                        "national": {
                          "type": "string"
                        },
                        "rfc3966": {
                          "type": "string"
                        }
                      }
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Finding"
                      }
                    }
                  }
                },
                "example": {
                  "input": "+33612345678",
                  "valid": true,
                  "possible": true,
                  "verdict": "Nothing notable",
                  "country_code": 33,
                  "region": "FR",
                  "line_type": "Mobile",
                  "allocated_operator": "Orange",
                  "allocated_operator_may_have_changed": true,
                  "formats": {
                    "e164": "+33612345678",
                    "international": "+33 6 12 34 56 78",
                    "national": "06 12 34 56 78",
                    "rfc3966": "tel:+33-6-12-34-56-78"
                  },
                  "findings": []
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/mac": {
      "get": {
        "tags": [
          "MAC Lookup"
        ],
        "summary": "Look up a MAC address",
        "operationId": "lookupMac",
        "description": "Vendor (from the IEEE OUI registry), unicast or multicast, universal or locally administered, randomized addresses, virtualization vendors, reserved ranges, the EUI-64 IPv6 interface ID and every common notation. `oui` is null for local or reserved addresses.\n\nOffline analysis: no scan job and no scan quota.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "mac",
            "in": "query",
            "required": true,
            "description": "MAC address in any common notation.",
            "schema": {
              "type": "string",
              "example": "00:1A:2B:3C:4D:5E"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Look up a MAC address",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mac": {
                      "type": "string"
                    },
                    "verdict": {
                      "type": "string"
                    },
                    "cast": {
                      "type": "string",
                      "enum": [
                        "Unicast",
                        "Multicast"
                      ]
                    },
                    "administration": {
                      "type": "string",
                      "enum": [
                        "Universal",
                        "Local"
                      ]
                    },
                    "randomized": {
                      "type": "boolean"
                    },
                    "oui": {
                      "type": "string",
                      "nullable": true
                    },
                    "vendor": {
                      "type": "string",
                      "nullable": true
                    },
                    "virtualization": {
                      "type": "string",
                      "nullable": true
                    },
                    "special": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "label": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    },
                    "eui64_ipv6": {
                      "type": "string"
                    },
                    "formats": {
                      "type": "object",
                      "properties": {
                        "colon": {
                          "type": "string"
                        },
                        "hyphen": {
                          "type": "string"
                        },
                        "cisco": {
                          "type": "string"
                        },
                        "bare": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "mac": "00:1a:2b:3c:4d:5e",
                  "verdict": "Vendor assigned",
                  "cast": "Unicast",
                  "administration": "Universal",
                  "randomized": false,
                  "oui": "00:1a:2b",
                  "vendor": "Ayecom Technology",
                  "virtualization": null,
                  "special": null,
                  "eui64_ipv6": "fe80::21a:2bff:fe3c:4d5e",
                  "formats": {
                    "colon": "00:1a:2b:3c:4d:5e",
                    "hyphen": "00-1a-2b-3c-4d-5e",
                    "cisco": "001a.2b3c.4d5e",
                    "bare": "001a2b3c4d5e"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/api/v1/scan/ioc": {
      "post": {
        "tags": [
          "IOC Extraction"
        ],
        "summary": "Extract IOCs from text",
        "operationId": "extractIocs",
        "description": "Pulls every indicator (IPs, domains, URLs, emails, hashes, crypto addresses, CVEs...) out of up to 1 MB of raw text, grouped by kind, each with its mlab analysis link. At most 500 are returned; `truncated` says when the list was cut.\n\n**Defanged text is read as written by people.** `hxxp://`, `hxxps://`, `[.]`, `(.)`, `[dot]`, `[:]`, `[at]` and `fxp://` are refanged before extraction, so `hxxp://evil[.]example[.]net/x` comes back as the URL `http://evil.example.net/x`. What mlab's own exports defang reads back as itself.\n\n**SMS threat scoring.** `risk=true` (or `fast`) adds a `risk` block from a fully offline scorer, fast enough for a filtering app's blocking path. `risk=deep` also unmasks shortened links and checks raw-IP hosts, which makes outbound requests: keep it off the blocking path.\n\n**API key permission:** `scan.lookup`.\n",
        "parameters": [
          {
            "name": "risk",
            "in": "query",
            "required": false,
            "description": "Add SMS threat scoring: `true`/`fast` (offline) or `deep` (network).",
            "schema": {
              "type": "string",
              "example": "true",
              "enum": [
                "true",
                "fast",
                "deep"
              ]
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Keyword and brand pack for the scorer. `pays` is accepted as an alias.",
            "schema": {
              "type": "string",
              "example": "fr"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "maxLength": 1048576
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Indicators grouped by kind.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "iocs": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "value": {
                              "type": "string"
                            },
                            "link": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "ioc_total": {
                      "type": "integer"
                    },
                    "truncated": {
                      "type": "boolean"
                    },
                    "risk": {
                      "type": "object",
                      "description": "Only with `risk`.",
                      "properties": {
                        "score": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 100
                        },
                        "band": {
                          "type": "string",
                          "enum": [
                            "clean",
                            "suspect",
                            "high",
                            "critical"
                          ]
                        },
                        "action": {
                          "type": "string",
                          "enum": [
                            "allow",
                            "quarantine",
                            "drop"
                          ]
                        },
                        "reasons": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "code": {
                                "type": "string"
                              },
                              "weight": {
                                "type": "integer"
                              },
                              "label": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "fast",
                            "deep"
                          ]
                        },
                        "country": {
                          "type": "string"
                        },
                        "deep": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          },
                          "description": "Per-URL resolution, `deep` only."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": "ok",
                  "ioc_total": 1,
                  "truncated": false,
                  "iocs": {
                    "url": [
                      {
                        "value": "https://colis-livraison.top/suivi",
                        "link": "https://mlab.sh/url?q=https://colis-livraison.top/suivi"
                      }
                    ]
                  },
                  "risk": {
                    "score": 85,
                    "band": "critical",
                    "action": "drop",
                    "mode": "fast",
                    "country": "fr",
                    "reasons": [
                      {
                        "code": "parcel_lure",
                        "weight": 30,
                        "label": "Parcel delivery lure"
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed input. The body carries the reason."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.lookup"
      }
    },
    "/upload/file": {
      "post": {
        "tags": [
          "File Scan"
        ],
        "summary": "Upload a file for analysis",
        "operationId": "uploadFile",
        "description": "Uploads a file (max 10 MB) and launches static-analysis jobs. Use the returned `sha256` with `/api/v1/scan/file/results` to fetch results.\n\n**Note:** this endpoint lives at the site root (`/upload/file`), not under `/api/v1`.\n\nSupported types: PNG, JPG, PDF, DOCX, XLSX, PPTX, EXE, DLL, SYS.\n\n**API key permission:** `scan.file`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file to analyze (max 10 MB)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File accepted and jobs launched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileUploadResponse"
                },
                "example": {
                  "success": true,
                  "filename": "0c2c2762-4c33-4b49-8771-be36675c8a58.png",
                  "sha256": "5f31de7b7059acf773ba2fafcd318a2f46883d58deb9b323e74dfb20453ed3d0",
                  "job_launched": true,
                  "existingData": [],
                  "existingJobs": []
                }
              }
            }
          },
          "400": {
            "description": "Bad request (invalid content type, no file, unsupported type).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadError"
                },
                "example": {
                  "success": false,
                  "error": {
                    "code": 400,
                    "message": "invalid_content_type"
                  }
                }
              }
            }
          },
          "413": {
            "description": "File exceeds 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadError"
                },
                "example": {
                  "success": false,
                  "error": {
                    "code": 413,
                    "message": "file_too_large"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadError"
                },
                "example": {
                  "success": false,
                  "error": {
                    "code": 429,
                    "message": "rate_limited"
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.file"
      }
    },
    "/api/v1/scan/file/results": {
      "get": {
        "tags": [
          "File Scan"
        ],
        "summary": "Get file analysis results",
        "operationId": "getFileResults",
        "description": "Returns file metadata and per-job analysis output. Poll until `status` is `completed`; the `analysis` array grows as jobs finish.\n\n**API key permission:** `scan.file`.\n",
        "parameters": [
          {
            "name": "sha256",
            "in": "query",
            "required": true,
            "description": "The `sha256` returned by the upload endpoint.",
            "schema": {
              "type": "string",
              "example": "5f31de7b7059acf773ba2fafcd318a2f46883d58deb9b323e74dfb20453ed3d0"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File analysis results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileResults"
                },
                "example": {
                  "status": "completed",
                  "jobs_total": 3,
                  "jobs_completed": 3,
                  "file": {
                    "sha256": "5f31de7b7059acf773ba2fafcd318a2f46883d58deb9b323e74dfb20453ed3d0",
                    "md5": "d41d8cd98f00b204e9800998ecf8427e",
                    "ssdeep": "3:tk:tk",
                    "filename": "0c2c2762-4c33-4b49-8771-be36675c8a58.png",
                    "size": 70,
                    "mime_type": "image/png",
                    "created_at": "2026-07-05T16:13:54Z"
                  },
                  "analysis": [
                    {
                      "job_name": "binwalk",
                      "end_date": "2026-07-05T16:14:04+00:00",
                      "data": "Analyzed 1 file for 111 file signatures (251 magic patterns) in 405.0 milliseconds"
                    },
                    {
                      "job_name": "exiftool",
                      "end_date": "2026-07-05T16:14:04+00:00",
                      "data": "File Type : PNG\nMIME Type : image/png\nImage Width : 1\nImage Height : 1"
                    },
                    {
                      "job_name": "strings",
                      "end_date": "2026-07-05T16:14:05+00:00",
                      "data": "IHDR\nIDAT\nIEND"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No results found for this hash yet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "example": {
                    "error": "Not found"
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "x-api-key-permission": "scan.file"
      }
    },
    "/api/v1/scan/file/output": {
      "get": {
        "tags": [
          "File Scan"
        ],
        "summary": "Get one tool's raw output",
        "operationId": "getFileToolOutput",
        "description": "The raw output of a single analysis tool for a file, kept out of `/scan/file/results` so that summary stays light. Use a tool name listed in the results.",
        "parameters": [
          {
            "name": "sha256",
            "in": "query",
            "required": true,
            "description": "SHA-256 of the analysed file.",
            "schema": {
              "type": "string",
              "example": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f"
            }
          },
          {
            "name": "tool",
            "in": "query",
            "required": true,
            "description": "Tool name, as listed in the file results.",
            "schema": {
              "type": "string",
              "example": "yara"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tool output.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sha256": {
                      "type": "string"
                    },
                    "tool": {
                      "type": "string"
                    },
                    "is_json": {
                      "type": "boolean",
                      "description": "When true, `output` is a JSON document serialized as a string."
                    },
                    "output": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed sha256 or tool."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No output for this file and tool."
          }
        }
      }
    },
    "/api/v1/domain/ssl": {
      "get": {
        "tags": [
          "SSL"
        ],
        "summary": "Get SSL certificate history",
        "operationId": "getDomainSsl",
        "description": "Returns SSL/TLS certificates observed for a domain (from certificate transparency logs). Returns an empty array if none are known.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/DomainQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Certificates for the domain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SslCert"
                  }
                },
                "example": [
                  {
                    "domain": "google.com",
                    "id": 13769145126,
                    "issuer_ca_id": 0,
                    "issuer_name": "C=US, O=Google Trust Services, CN=WR4",
                    "common_name": "da-twd-8.da.ext.google.com",
                    "name_value": "da-twd-8.da.ext.google.com",
                    "entry_timestamp": "2026-02-11T21:22:07Z",
                    "not_before": "2026-02-11T21:22:07Z",
                    "not_after": "2026-05-12T21:22:06Z",
                    "serial_number": "",
                    "result_count": 1
                  }
                ]
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/limit/{scan_type}": {
      "get": {
        "tags": [
          "Limits"
        ],
        "summary": "Check remaining quota",
        "operationId": "getLimit",
        "description": "Returns the remaining and total daily quota for a scan type. Limits are per organization, shared across members, and reset daily.\n",
        "parameters": [
          {
            "name": "scan_type",
            "in": "path",
            "required": true,
            "description": "The scan type to check.",
            "schema": {
              "type": "string",
              "enum": [
                "domain",
                "ip",
                "file",
                "crypto"
              ],
              "example": "domain"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Quota for the scan type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitResponse"
                },
                "example": {
                  "scan_type": "domain",
                  "remaining": 98,
                  "total": 100
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/scan/batch": {
      "post": {
        "tags": [
          "Batch Triage"
        ],
        "summary": "Triage a list of indicators",
        "operationId": "triageBatch",
        "description": "Parses up to 1 MB of pasted text (one indicator per line, or any free text), keeps at most 200 distinct indicators and returns what mlab already knows about each, from cache: no scan quota is spent. `skipped` lists what was not read as an indicator, with the reason.\n\n**API key permission:** `scan.batch`.\n",
        "responses": {
          "200": {
            "description": "One row per indicator.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "truncated": {
                      "type": "boolean"
                    },
                    "indicators": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          },
                          "link": {
                            "type": "string"
                          },
                          "known": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "value": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "count": 1,
                  "truncated": false,
                  "indicators": [
                    {
                      "kind": "ip",
                      "value": "8.8.8.8",
                      "link": "https://mlab.sh/ip/8.8.8.8",
                      "known": true
                    }
                  ],
                  "skipped": []
                }
              }
            }
          },
          "400": {
            "description": "No indicator, or more than 1 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "indicators"
                ],
                "properties": {
                  "indicators": {
                    "type": "string",
                    "maxLength": 1048576,
                    "description": "The pasted text."
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "scan.batch"
      }
    },
    "/api/v1/scan/file/bash/ai": {
      "post": {
        "tags": [
          "Script Analysis"
        ],
        "summary": "AI analysis of a shell script",
        "operationId": "analyzeScriptAi",
        "description": "Explains in prose what a previously submitted shell script does. The script must have been submitted by an authenticated caller (a member or an API key), since only then is its content kept. Cached per script: a cached result costs nothing, a new one counts against the daily AI analysis quota.\n\n**API key permission:** `scan.file`.\n",
        "responses": {
          "200": {
            "description": "The analysis, as Markdown and safe HTML.",
            "content": {
              "application/json": {}
            }
          },
          "400": {
            "description": "Invalid sha256, or the script content was not kept.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "sha256",
            "in": "query",
            "required": true,
            "description": "SHA-256 of the submitted script.",
            "schema": {
              "type": "string",
              "example": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
            }
          }
        ],
        "x-api-key-permission": "scan.file"
      }
    },
    "/api/v1/scan/redkit": {
      "post": {
        "tags": [
          "RedKit"
        ],
        "summary": "Launch a RedKit audit",
        "operationId": "launchRedkit",
        "description": "Starts a RedKit web audit of a domain with the chosen plugins. Counts against the plan's RedKit quota (monthly on paid plans). The recap email goes to the organization's first user when the audit was launched with an API key.\n\n**API key permission:** `redkit`.\n",
        "responses": {
          "200": {
            "description": "Audit started.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "scan_uuid": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "job_id": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "status": "success",
                  "scan_uuid": "2f1c0000-0000-4000-8000-000000000000",
                  "job_id": "7d2e0000-0000-4000-8000-000000000000"
                }
              }
            }
          },
          "400": {
            "description": "Quota reached, invalid domain or unknown plugin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "plugins"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "example": "example.com"
                  },
                  "plugins": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "headers",
                      "ssl",
                      "dns"
                    ]
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "redkit"
      }
    },
    "/api/v1/scan/redkit/status": {
      "get": {
        "tags": [
          "RedKit"
        ],
        "summary": "Status of a RedKit audit",
        "operationId": "getRedkitStatus",
        "description": "The audit's status; once `completed`, its raw results in `raw_json`. Only audits of the caller's organization are visible.\n\n**API key permission:** `redkit`.\n",
        "responses": {
          "200": {
            "description": "Status, and results when completed.",
            "content": {
              "application/json": {
                "example": {
                  "status": "completed",
                  "raw_json": "{...}"
                }
              }
            }
          },
          "404": {
            "description": "No such audit in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "uuid",
            "in": "query",
            "required": true,
            "description": "The `scan_uuid` returned at launch.",
            "schema": {
              "type": "string",
              "example": "2f1c0000-0000-4000-8000-000000000000"
            }
          }
        ],
        "x-api-key-permission": "redkit"
      }
    },
    "/api/v1/scan/redkit/quota": {
      "get": {
        "tags": [
          "RedKit"
        ],
        "summary": "RedKit quota",
        "operationId": "getRedkitQuota",
        "description": "The organization's RedKit quota. `limit` is -1 when unlimited.\n\n**API key permission:** `redkit`.\n",
        "responses": {
          "200": {
            "description": "Quota.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string"
                    },
                    "used": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "remaining": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "plan": "team",
                  "used": 3,
                  "limit": 20,
                  "remaining": 17
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-api-key-permission": "redkit"
      }
    },
    "/api/v1/scan/watchdog/list": {
      "get": {
        "tags": [
          "Scheduled Scans"
        ],
        "summary": "List scheduled scans",
        "operationId": "listSchedules",
        "description": "Every scheduled scan of the organization.\n\n**API key permission:** `watchdog.read`.\n",
        "responses": {
          "200": {
            "description": "The schedules.",
            "content": {
              "application/json": {
                "example": {
                  "schedules": [
                    {
                      "uuid": "…",
                      "scan_type": "domain",
                      "domain": "example.com",
                      "plugins": [],
                      "frequency": "weekly",
                      "day_of_week": 1,
                      "day_of_month": null,
                      "hour": 3,
                      "minute": 0,
                      "enabled": true,
                      "next_run_at": "2026-10-05 03:00:00",
                      "last_run_at": "",
                      "created_at": "2026-10-01 17:53:03"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-api-key-permission": "watchdog.read"
      }
    },
    "/api/v1/scan/watchdog/create": {
      "post": {
        "tags": [
          "Scheduled Scans"
        ],
        "summary": "Create a scheduled scan",
        "operationId": "createSchedule",
        "description": "Schedules a recurring `domain` scan or `redkit` audit. A RedKit schedule needs a domain verified in the organization's infrastructure and at least one plugin. Weekly schedules need `day_of_week` (0 = Monday), monthly ones `day_of_month` (1 to 28). Times are UTC. A schedule created with an API key is owned by the organization's first user, who receives its notifications.\n\n**API key permission:** `watchdog.write`.\n",
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success",
                  "uuid": "…"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "frequency"
                ],
                "properties": {
                  "scan_type": {
                    "type": "string",
                    "enum": [
                      "domain",
                      "redkit"
                    ],
                    "default": "redkit"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "frequency": {
                    "type": "string",
                    "enum": [
                      "weekly",
                      "monthly"
                    ]
                  },
                  "plugins": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "RedKit plugins (redkit only)."
                  },
                  "day_of_week": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 6
                  },
                  "day_of_month": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 28
                  },
                  "hour": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 23,
                    "default": 9
                  },
                  "minute": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 59,
                    "default": 0
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "watchdog.write"
      }
    },
    "/api/v1/scan/watchdog/update": {
      "post": {
        "tags": [
          "Scheduled Scans"
        ],
        "summary": "Pause or resume a scheduled scan",
        "operationId": "updateSchedule",
        "description": "Turns a schedule on or off.\n\n**API key permission:** `watchdog.write`.\n",
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success",
                  "message": "Schedule updated."
                }
              }
            }
          },
          "404": {
            "description": "No such schedule in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "watchdog.write"
      }
    },
    "/api/v1/scan/watchdog/delete": {
      "post": {
        "tags": [
          "Scheduled Scans"
        ],
        "summary": "Delete a scheduled scan",
        "operationId": "deleteSchedule",
        "description": "Deletes a schedule.\n\n**API key permission:** `watchdog.write`.\n",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "404": {
            "description": "No such schedule in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "watchdog.write"
      }
    },
    "/api/v1/cases": {
      "get": {
        "tags": [
          "Cases"
        ],
        "summary": "List cases",
        "operationId": "listCases",
        "description": "Cases are part of the Team and Enterprise plans. Every case of the organization, open ones first.\n\n**API key permission:** `cases.read`.\n",
        "responses": {
          "200": {
            "description": "The cases.",
            "content": {
              "application/json": {
                "example": {
                  "cases": [
                    {
                      "uuid": "…",
                      "title": "Phishing wave",
                      "summary": "",
                      "status": "open",
                      "indicators": 4,
                      "comments": 2,
                      "created_at": "2026-10-01 17:53:03",
                      "updated_at": "2026-10-01 17:55:10"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The plan has no cases.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-api-key-permission": "cases.read"
      }
    },
    "/api/v1/cases/detail": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Read a case",
        "operationId": "getCase",
        "description": "Cases are part of the Team and Enterprise plans. One case with its indicators and comments. A comment written with an API key is signed `API key · <key name>`.\n\n**API key permission:** `cases.read`.\n",
        "responses": {
          "200": {
            "description": "The case.",
            "content": {
              "application/json": {
                "example": {
                  "uuid": "…",
                  "title": "Phishing wave",
                  "summary": "",
                  "status": "open",
                  "indicators": [
                    {
                      "uuid": "…",
                      "kind": "domain",
                      "value": "evil.example",
                      "link": "https://mlab.sh/domain/evil.example",
                      "added_at": "…"
                    }
                  ],
                  "comments": [
                    {
                      "uuid": "…",
                      "author": "API key · SOAR prod",
                      "body": "Seen in 12 mailboxes",
                      "indicator_uuid": ""
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such case in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.read"
      }
    },
    "/api/v1/cases/create": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Create a case",
        "operationId": "createCase",
        "description": "Cases are part of the Team and Enterprise plans. Creates a case. A case created with an API key is authored by the key.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success",
                  "uuid": "…"
                }
              }
            }
          },
          "400": {
            "description": "Missing title, or the organization holds the maximum number of cases.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "summary": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/update": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Update a case",
        "operationId": "updateCase",
        "description": "Cases are part of the Team and Enterprise plans. Changes a case's title, summary or status.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "404": {
            "description": "No such case in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  },
                  "title": {
                    "type": "string"
                  },
                  "summary": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "open",
                      "closed"
                    ]
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/delete": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Delete a case",
        "operationId": "deleteCase",
        "description": "Cases are part of the Team and Enterprise plans. Deletes a case. An API key may delete only what it created itself; a member needs to be its author, an owner or an admin.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such case in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/indicator/add": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Add indicators to a case",
        "operationId": "addCaseIndicators",
        "description": "Cases are part of the Team and Enterprise plans. Adds the indicators found in free text. Each one not added is returned in `skipped` with the reason.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "What was added and skipped.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success",
                  "added": 2,
                  "skipped": [
                    {
                      "value": "not-an-ioc",
                      "reason": "not an indicator"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such case in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid",
                  "indicators"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  },
                  "indicators": {
                    "type": "string",
                    "description": "Free text, one or more indicators."
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/indicator/delete": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Remove an indicator from a case",
        "operationId": "deleteCaseIndicator",
        "description": "Cases are part of the Team and Enterprise plans. Same rule as deleting a case: an API key removes only what it added.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "404": {
            "description": "No such case or indicator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid",
                  "indicator_uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  },
                  "indicator_uuid": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/comment/add": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Comment on a case",
        "operationId": "addCaseComment",
        "description": "Cases are part of the Team and Enterprise plans. Adds a comment, optionally on one indicator.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Added.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "404": {
            "description": "No such case in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid",
                  "body"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  },
                  "body": {
                    "type": "string"
                  },
                  "indicator_uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional: comment on this indicator."
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/cases/comment/delete": {
      "post": {
        "tags": [
          "Cases"
        ],
        "summary": "Delete a comment",
        "operationId": "deleteCaseComment",
        "description": "Cases are part of the Team and Enterprise plans. Same rule as deleting a case: an API key deletes only its own comments.\n\n**API key permission:** `cases.write`.\n",
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "404": {
            "description": "No such case or comment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uuid",
                  "comment_uuid"
                ],
                "properties": {
                  "uuid": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The case."
                  },
                  "comment_uuid": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "x-api-key-permission": "cases.write"
      }
    },
    "/api/v1/scan/finding-tags": {
      "get": {
        "tags": [
          "Finding Tags"
        ],
        "summary": "List finding tags",
        "operationId": "listFindingTags",
        "description": "The tags set on an audit's findings.\n\n**API key permission:** `tags.read`.\n",
        "responses": {
          "200": {
            "description": "The tags.",
            "content": {
              "application/json": {
                "example": {
                  "tags": [
                    {
                      "finding_id": "9f3b2c1a7d4e5f60",
                      "tag": "false-positive",
                      "user_uuid": "…"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing or foreign scan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "scan_uuid",
            "in": "query",
            "required": true,
            "description": "The RedKit audit.",
            "schema": {
              "type": "string",
              "example": "2f1c0000-0000-4000-8000-000000000000"
            }
          }
        ],
        "x-api-key-permission": "tags.read"
      }
    },
    "/api/v1/scan/finding-tags/add": {
      "get": {
        "tags": [
          "Finding Tags"
        ],
        "summary": "Tag a finding",
        "operationId": "addFindingTag",
        "description": "Sets a tag on a finding. A tag set with an API key is credited to the key.\n\n**API key permission:** `tags.write`.\n",
        "responses": {
          "200": {
            "description": "Tagged.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "400": {
            "description": "Invalid tag or finding, or foreign scan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "scan_uuid",
            "in": "query",
            "required": true,
            "description": "The RedKit audit.",
            "schema": {
              "type": "string",
              "example": "2f1c0000-0000-4000-8000-000000000000"
            }
          },
          {
            "name": "finding_id",
            "in": "query",
            "required": true,
            "description": "The finding, as shown on the result page.",
            "schema": {
              "type": "string",
              "example": "9f3b2c1a7d4e5f60"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "false-positive",
                "accepted-risk",
                "fixed",
                "review-later"
              ]
            }
          }
        ],
        "x-api-key-permission": "tags.write"
      }
    },
    "/api/v1/scan/finding-tags/remove": {
      "get": {
        "tags": [
          "Finding Tags"
        ],
        "summary": "Untag a finding",
        "operationId": "removeFindingTag",
        "description": "Removes a tag from a finding.\n\n**API key permission:** `tags.write`.\n",
        "responses": {
          "200": {
            "description": "Untagged.",
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "400": {
            "description": "Invalid tag or finding, or foreign scan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "parameters": [
          {
            "name": "scan_uuid",
            "in": "query",
            "required": true,
            "description": "The RedKit audit.",
            "schema": {
              "type": "string",
              "example": "2f1c0000-0000-4000-8000-000000000000"
            }
          },
          {
            "name": "finding_id",
            "in": "query",
            "required": true,
            "description": "The finding, as shown on the result page.",
            "schema": {
              "type": "string",
              "example": "9f3b2c1a7d4e5f60"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "false-positive",
                "accepted-risk",
                "fixed",
                "review-later"
              ]
            }
          }
        ],
        "x-api-key-permission": "tags.write"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Your API key, prefixed with `token `. Example: `Authorization: token mlab_your_api_key_here`. Each key only reaches the endpoints its permissions allow.\n"
      }
    },
    "parameters": {
      "DomainQuery": {
        "name": "domain",
        "in": "query",
        "required": true,
        "description": "The domain in question.",
        "schema": {
          "type": "string",
          "example": "example.com"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Invalid or missing API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "status": "error",
              "message": "Invalid or missing API key."
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key lacks the permission this endpoint needs, or the endpoint is not open to API keys (organization and account settings never are).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "This API key does not have the 'cases.write' permission (Write cases). An owner or admin can add it in the organization settings."
            }
          }
        }
      }
    },
    "schemas": {
      "Greeting": {
        "type": "object",
        "properties": {
          "auth": {
            "type": "string",
            "description": "Authentication method used.",
            "example": "api_key"
          },
          "message": {
            "type": "string",
            "example": "Hello, Acme Corp!"
          },
          "organization": {
            "type": "string",
            "example": "Acme Corp"
          },
          "plan": {
            "type": "string",
            "description": "The organization's subscription plan.",
            "enum": [
              "anonymous",
              "free",
              "pro",
              "team",
              "enterprise"
            ],
            "example": "pro"
          }
        }
      },
      "ScanStarted": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "message": {
            "type": "string",
            "example": "Domain scan has been started."
          }
        }
      },
      "ScanStatus": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "scanning",
              "success"
            ],
            "description": "Scan progress:\n- `pending` - queued, not started\n- `scanning` - actively running\n- `success` - completed\n"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "message"
        ]
      },
      "DomainResults": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "scan_date": {
            "type": "string",
            "example": "2026-07-05 16:13:00 UTC"
          },
          "status": {
            "type": "string",
            "description": "`completed` or `in_progress`.",
            "example": "completed"
          },
          "results": {
            "type": "object",
            "properties": {
              "subdomains": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "All discovered subdomains."
              },
              "subdomains_suspicious": {
                "type": "array",
                "description": "Subdomains flagged by keyword.",
                "items": {
                  "type": "object",
                  "properties": {
                    "keyword": {
                      "type": "string"
                    },
                    "subdomain": {
                      "type": "string"
                    }
                  }
                }
              },
              "dns": {
                "type": "object",
                "properties": {
                  "resolve": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DnsResolve"
                    }
                  },
                  "txt": {
                    "$ref": "#/components/schemas/TxtRecords"
                  }
                }
              },
              "ssl": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SslCert"
                }
              },
              "files": {
                "type": "object",
                "properties": {
                  "robots_txt": {
                    "type": "string",
                    "description": "Contents of robots.txt (empty if not found)."
                  },
                  "security_txt": {
                    "type": "string",
                    "description": "Contents of security.txt (empty if not found)."
                  }
                }
              }
            }
          }
        }
      },
      "DnsResolve": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "a": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IPv4 A records."
          },
          "aaaa": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IPv6 AAAA records."
          },
          "cname": {
            "type": [
              "string",
              "null"
            ],
            "description": "CNAME target, if any."
          }
        }
      },
      "TxtRecords": {
        "type": "object",
        "properties": {
          "raw": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "spf": {
            "type": [
              "string",
              "null"
            ]
          },
          "dmarc": {
            "type": [
              "string",
              "null"
            ]
          },
          "dkim": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SslCert": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "issuer_ca_id": {
            "type": "integer"
          },
          "issuer_name": {
            "type": "string"
          },
          "common_name": {
            "type": "string"
          },
          "name_value": {
            "type": "string",
            "description": "Subject alternative names (newline-separated)."
          },
          "entry_timestamp": {
            "type": "string"
          },
          "not_before": {
            "type": "string"
          },
          "not_after": {
            "type": "string"
          },
          "serial_number": {
            "type": "string"
          },
          "result_count": {
            "type": "integer"
          }
        }
      },
      "IpInfo": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string"
          },
          "reserved": {
            "type": "boolean",
            "example": false
          },
          "isp": {
            "type": "string"
          },
          "org": {
            "type": "string"
          },
          "as": {
            "type": "string",
            "description": "Autonomous System number and name."
          },
          "city": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "country_code": {
            "type": "string"
          },
          "continent": {
            "type": "string"
          },
          "continent_code": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "lat": {
            "type": "number"
          },
          "lon": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "example": "success"
          },
          "proxy": {
            "type": "boolean",
            "description": "Address belongs to a known proxy, VPN or anonymizing service."
          },
          "hosting": {
            "type": "boolean",
            "description": "Address belongs to a datacenter or hosting provider rather than an access network."
          },
          "mobile": {
            "type": "boolean",
            "description": "Address belongs to a mobile carrier network."
          },
          "rdns": {
            "$ref": "#/components/schemas/Rdns"
          },
          "rdap": {
            "$ref": "#/components/schemas/RdapIp"
          },
          "tor": {
            "type": "object",
            "description": "Tor exit-node status.",
            "properties": {
              "available": {
                "type": "boolean"
              },
              "ip": {
                "type": "string"
              },
              "is_tor": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "ReservedIp": {
        "type": "object",
        "description": "Response shape for private/reserved IP ranges.",
        "properties": {
          "ip": {
            "type": "string"
          },
          "reserved": {
            "type": "boolean",
            "example": true
          },
          "type": {
            "type": "string",
            "example": "ipv4"
          },
          "range": {
            "type": "string",
            "example": "Private-use (192.168.0.0/16)"
          },
          "rfc": {
            "type": "string",
            "example": "RFC 1918"
          }
        }
      },
      "CryptoInfo": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "chain": {
            "type": "string",
            "description": "Chain ID."
          },
          "type": {
            "type": "string",
            "enum": [
              "eoa",
              "contract",
              "unknown"
            ]
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "e.g. exchange, defi, mixer, scam, sanctioned."
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "source": {
                  "type": "string"
                }
              }
            }
          },
          "sanctions": {
            "type": "object",
            "properties": {
              "is_sanctioned": {
                "type": "boolean"
              },
              "authority": {
                "type": "string"
              },
              "list_date": {
                "type": "string"
              }
            }
          },
          "risk_score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FileUploadResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "filename": {
            "type": "string",
            "description": "Stored filename (UUID + extension)."
          },
          "sha256": {
            "type": "string"
          },
          "job_launched": {
            "type": "boolean"
          },
          "existingData": {
            "type": "array",
            "description": "Prior analysis data if the file was seen before.",
            "items": {
              "type": "object"
            }
          },
          "existingJobs": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "UploadError": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string",
                "description": "One of `invalid_content_type`, `no_file`, `file_too_large`, `rate_limited`, `storage_error`.\n"
              }
            }
          }
        }
      },
      "FileResults": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "in_progress",
              "completed"
            ]
          },
          "jobs_total": {
            "type": "integer"
          },
          "jobs_completed": {
            "type": "integer"
          },
          "file": {
            "type": "object",
            "properties": {
              "sha256": {
                "type": "string"
              },
              "md5": {
                "type": "string"
              },
              "ssdeep": {
                "type": "string"
              },
              "filename": {
                "type": "string"
              },
              "size": {
                "type": "integer",
                "format": "int64"
              },
              "mime_type": {
                "type": "string"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "analysis": {
            "type": "array",
            "description": "One entry per analysis job.",
            "items": {
              "type": "object",
              "properties": {
                "job_name": {
                  "type": "string",
                  "description": "e.g. binwalk, exiftool, strings."
                },
                "end_date": {
                  "type": "string"
                },
                "data": {
                  "description": "Decoded analysis output (string or object)."
                }
              }
            }
          }
        }
      },
      "NetworkCapture": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "captureMs": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "totalBytes": {
            "type": "integer"
          },
          "requests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NetworkRequest"
            }
          }
        }
      },
      "NetworkRequest": {
        "type": "object",
        "properties": {
          "requestId": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "resourceType": {
            "type": "string",
            "description": "Document, Script, XHR, Image, Stylesheet, …"
          },
          "initiator": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status (0 if failed)."
          },
          "mimeType": {
            "type": "string"
          },
          "protocol": {
            "type": "string"
          },
          "remoteIP": {
            "type": "string"
          },
          "encodedBytes": {
            "type": "integer"
          },
          "startMs": {
            "type": "integer"
          },
          "endMs": {
            "type": "integer"
          },
          "requestHeaders": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "responseHeaders": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "timing": {
            "type": "object",
            "description": "Low-level CDP timing breakdown."
          },
          "failed": {
            "type": "boolean"
          },
          "errorText": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LimitResponse": {
        "type": "object",
        "properties": {
          "scan_type": {
            "type": "string",
            "enum": [
              "domain",
              "ip",
              "file",
              "crypto"
            ]
          },
          "remaining": {
            "type": "integer",
            "description": "Scans remaining for the current day."
          },
          "total": {
            "type": "integer",
            "description": "Total daily limit for your plan."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "error"
          },
          "message": {
            "type": "string",
            "example": "Description of what went wrong."
          }
        }
      },
      "CidrRange": {
        "type": "object",
        "description": "A network range, computed from the address bits. Offline: no upstream lookup, no quota.",
        "properties": {
          "cidr": {
            "type": "string",
            "description": "The range in canonical network/prefix form.",
            "example": "10.0.0.0/8"
          },
          "input": {
            "type": "string",
            "description": "The value as supplied."
          },
          "input_is_network": {
            "type": "boolean",
            "description": "False when the input named a host inside the range rather than the network itself."
          },
          "version": {
            "type": "string",
            "enum": [
              "ipv4",
              "ipv6"
            ]
          },
          "prefix_length": {
            "type": "integer",
            "example": 8
          },
          "host_bits": {
            "type": "integer",
            "example": 24
          },
          "network": {
            "type": "string",
            "example": "10.0.0.0"
          },
          "broadcast": {
            "type": "string",
            "nullable": true,
            "description": "IPv4 only, and absent for a /31 and /32. IPv6 has no broadcast address."
          },
          "first_usable": {
            "type": "string",
            "example": "10.0.0.1"
          },
          "last_usable": {
            "type": "string",
            "example": "10.255.255.254"
          },
          "netmask": {
            "type": "string",
            "nullable": true,
            "description": "IPv4 only.",
            "example": "255.0.0.0"
          },
          "wildcard": {
            "type": "string",
            "nullable": true,
            "description": "IPv4 only.",
            "example": "0.255.255.255"
          },
          "total_addresses": {
            "type": "string",
            "description": "Decimal string: an IPv6 range can hold more addresses than a 64-bit integer can count.",
            "example": "16777216"
          },
          "usable_hosts": {
            "type": "string",
            "example": "16777214"
          },
          "reserved": {
            "type": "object",
            "nullable": true,
            "description": "Set when the range sits inside a reserved block. Null for a routable range.",
            "properties": {
              "range": {
                "type": "string"
              },
              "rfc": {
                "type": "string"
              }
            }
          }
        }
      },
      "Rdns": {
        "type": "object",
        "description": "Reverse DNS for the address, with forward confirmation. A PTR record is a claim by whoever runs the address block; `forward_confirmed` says whether the name resolves back.",
        "properties": {
          "ip": {
            "type": "string"
          },
          "found": {
            "type": "boolean",
            "description": "A PTR record exists."
          },
          "names": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "The first PTR name."
          },
          "forward_confirmed": {
            "type": "boolean",
            "nullable": true,
            "description": "True when the name resolves back to this address. Null means the check could not be made, which is not the same as a failed check."
          },
          "forward_addresses": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the forward lookup returned, so a mismatch can be inspected."
          }
        }
      },
      "RdapIp": {
        "type": "object",
        "description": "The RIR allocation record for the block the address sits in.",
        "properties": {
          "ip": {
            "type": "string"
          },
          "found": {
            "type": "boolean"
          },
          "handle": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Network name as registered, e.g. GOGL."
          },
          "allocation_type": {
            "type": "string",
            "nullable": true,
            "example": "DIRECT ALLOCATION"
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "start_address": {
            "type": "string",
            "nullable": true
          },
          "end_address": {
            "type": "string",
            "nullable": true
          },
          "cidr": {
            "type": "string",
            "nullable": true,
            "description": "The block in CIDR form, when the registry publishes it."
          },
          "registered": {
            "type": "string",
            "nullable": true
          },
          "updated": {
            "type": "string",
            "nullable": true
          },
          "holder": {
            "type": "string",
            "nullable": true,
            "description": "Organisation the block is allocated to."
          },
          "abuse_name": {
            "type": "string",
            "nullable": true
          },
          "abuse_email": {
            "type": "string",
            "nullable": true,
            "description": "Where to report abuse originating from this address."
          },
          "abuse_phone": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CryptoAddress": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "chain": {
            "type": "string",
            "example": "BTC"
          },
          "chain_source": {
            "type": "string",
            "enum": [
              "detected",
              "explicit",
              "default"
            ],
            "description": "Only `detected` is a fact about the address. `default` means one was assumed."
          },
          "chain_ambiguous": {
            "type": "boolean",
            "description": "True when the family is known but the chain is not derivable, and you did not name one."
          },
          "chain_candidates": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Every chain this address could belong to. One entry unless it is EVM."
          },
          "address_info": {
            "type": "object",
            "properties": {
              "family": {
                "type": "string",
                "enum": [
                  "evm",
                  "btc",
                  "doge",
                  "trx",
                  "sol",
                  "ton"
                ],
                "nullable": true
              },
              "kind": {
                "type": "string",
                "enum": [
                  "p2pkh",
                  "p2sh",
                  "segwit",
                  "taproot",
                  "account"
                ]
              },
              "checksum": {
                "type": "string",
                "enum": [
                  "valid",
                  "invalid",
                  "absent",
                  "unverifiable"
                ],
                "description": "`absent` means the format carries none (Solana, lowercase EVM). `unverifiable` means EIP-55, which needs keccak256. Neither is a pass."
              },
              "testnet": {
                "type": "boolean"
              }
            }
          },
          "note": {
            "type": "string",
            "nullable": true,
            "description": "Present only when the chain was assumed, explaining what that costs."
          },
          "intel": {
            "type": "object",
            "description": "The crypto-intel body, passed through untouched: type, categories, labels, sanctions, risk_score, risk_level, checked_at."
          }
        }
      },
      "Finding": {
        "type": "object",
        "properties": {
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "HashVerdict": {
        "type": "object",
        "properties": {
          "hash": {
            "type": "string"
          },
          "algorithm": {
            "type": "string",
            "enum": [
              "MD5",
              "SHA-1",
              "SHA-256",
              "SHA-512"
            ]
          },
          "verdict": {
            "type": "string",
            "enum": [
              "known_good",
              "known_malicious",
              "unknown",
              "unavailable",
              "not_queryable",
              "disabled"
            ]
          },
          "found": {
            "type": "boolean"
          },
          "known_malicious": {
            "type": "boolean"
          },
          "matched_on": {
            "type": "string"
          },
          "enrichment_source": {
            "type": "string",
            "nullable": true,
            "description": "Which feed answered."
          },
          "source": {
            "type": "string"
          },
          "product": {
            "type": "string"
          },
          "family": {
            "type": "string"
          },
          "file_name": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "cached": {
            "type": "boolean"
          },
          "checked_at": {
            "type": "string",
            "nullable": true
          },
          "sources_hit": {
            "type": "integer"
          },
          "sources_answered": {
            "type": "integer"
          },
          "sources_queried": {
            "type": "integer"
          },
          "sources": {
            "type": "object",
            "description": "Each feed's own answer."
          },
          "mlab_sample": {
            "type": "object",
            "description": "The mlab file analysis of this sample, when one exists."
          },
          "hint": {
            "type": "string"
          }
        }
      }
    }
  }
}
