{"openapi":"3.1.0","info":{"title":"ContrastAPI","description":"Security intelligence API for AI models and developers. CVE lookup, domain intelligence, and code security verification.","version":"1.36.2"},"servers":[{"url":"https://api.contrastcyber.com"}],"paths":{"/v1/domain/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Domain Report","description":"Full domain intelligence report with DNS, WHOIS, SSL, subdomains, WAF. Use ?lite=true for fast subset.","operationId":"domain_report","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."},{"name":"lite","in":"query","required":false,"schema":{"type":"boolean","description":"Fast subset mode. When true, skips WHOIS, subdomains, CT logs, URLhaus, and reputation. Returns in ~250ms instead of 3-10s. Use for high-volume triage.","default":false,"title":"Lite"},"description":"Fast subset mode. When true, skips WHOIS, subdomains, CT logs, URLhaus, and reputation. Returns in ~250ms instead of 3-10s. Use for high-volume triage."},{"name":"include_all_txt","in":"query","required":false,"schema":{"type":"boolean","description":"Return every TXT record (default: only SPF, DMARC, DKIM, MTA-STS, TLS-RPT). total_txt_records under dns.* is always emitted with the honest pre-filter count. Default filter strips vendor verification strings (google-site-verification, ms=, facebook-domain-verification, etc.) that bloat reports without security signal. Pass include_all_txt=true only when you need the raw TXT inventory — for SPF/DMARC auditing the default is sufficient.","default":false,"title":"Include All Txt"},"description":"Return every TXT record (default: only SPF, DMARC, DKIM, MTA-STS, TLS-RPT). total_txt_records under dns.* is always emitted with the honest pre-filter count. Default filter strips vendor verification strings (google-site-verification, ms=, facebook-domain-verification, etc.) that bloat reports without security signal. Pass include_all_txt=true only when you need the raw TXT inventory — for SPF/DMARC auditing the default is sufficient."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Dns Records","description":"DNS record lookup: A, AAAA, MX, NS, TXT, CNAME, SOA.","operationId":"dns_records","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/email/mx/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Email Mx","description":"Email MX analysis — mail provider detection, SPF/DMARC/DKIM check, security grade.","operationId":"email_mx","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailMxResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/email/security-posture/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Email Security Posture","description":"Email authentication posture: SPF, DMARC, DKIM with numeric score and findings.","operationId":"email_security_posture_v1_email_security_posture__domain__get","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."},{"name":"selectors","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selectors"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailSecurityPostureResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/email/disposable/{email}":{"get":{"tags":["Domain Intelligence"],"summary":"Email Disposable","description":"Check if an email uses a disposable/temporary email provider.","operationId":"email_disposable","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","description":"Email address to check, e.g. 'user@example.com'. The local-part is preserved in the response but only the domain is checked against the disposable-provider database and MX records.","title":"Email"},"description":"Email address to check, e.g. 'user@example.com'. The local-part is preserved in the response but only the domain is checked against the disposable-provider database and MX records."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisposableResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/email/verify/{email}":{"get":{"tags":["Domain Intelligence"],"summary":"Email Verify Endpoint","description":"Combined email validation: syntax + MX + disposable + role + free-provider.\n\nCombines `email_mx` (MX resolution) and `email_disposable` (disposable check)\ninto a single call so AI agents auditing a contact list don't need to\ninterleave two tools. Adds role-address detection (admin@, info@, ...) and\nfree-provider classification (gmail/outlook/yahoo/...).\n\nDeliberately does NOT do SMTP `RCPT TO` deliverability probing — see the\nresponse model docstring for the rationale.","operationId":"email_verify","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","description":"Email address to verify, e.g. 'admin@example.com'. The local-part is preserved (lowercased) in the response; only the domain is hit for MX resolution + disposable lookup. NO SMTP RCPT TO probe is performed — see EmailVerifyResponse docstring.","title":"Email"},"description":"Email address to verify, e.g. 'admin@example.com'. The local-part is preserved (lowercased) in the response; only the domain is hit for MX resolution + disposable lookup. NO SMTP RCPT TO probe is performed — see EmailVerifyResponse docstring."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/robots/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Robots Txt Endpoint","description":"Fetch + parse the target domain's robots.txt file.\n\nReturns sitemaps, per-User-agent allow/disallow rules, crawl-delay, and the\nHost directive. Status 404 from the target = no robots.txt = implicit\nallow-all (RFC 9309 §2.4); the response carries `status_code: 404`,\n`user_agents: {}`, and an empty `sitemaps`/`host`.\n\nPer-target eTLD+1 throttle (60 req/min): a single Pro key cannot weaponise\nthe API against one site by spamming this endpoint; subdomain rotation\ncollapses to the same eTLD+1 bucket.","operationId":"robots_txt","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RobotsTxtResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/redirect/{url}":{"get":{"tags":["Domain Intelligence"],"summary":"Redirect Chain Endpoint","description":"Walk a URL's HTTP redirect chain hop-by-hop, returning each (status, Location, latency).\n\nUp to 10 hops; SSRF-safe (private IPs and non-HTTP schemes rejected at every\nhop, not just the start). Per-target eTLD+1 throttle (60 req/min) is consumed\nonce for the start URL and once for every *new* host reached in the chain —\na chain across 11 unrelated domains can't bypass the cap.\n\nPass the URL inline in the path (greedy-matched), e.g.\nGET /v1/redirect/https://bit.ly/3xyz — FastAPI accepts the literal `://`.","operationId":"redirect_chain","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"url","in":"path","required":true,"schema":{"type":"string","title":"Url"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedirectChainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brand/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Brand Assets Endpoint","description":"Scrape the target domain's homepage `<head>` for public brand assets:\nfavicon, og:image, theme-color, og:site_name, JSON-LD Organization.logo.\n\nEthical floor (Guardrail #3 in v1.25.0 plan): we honour the target's\nrobots.txt — `Disallow: /` for our UA token (\"ContrastAPI\") OR for `*`\nreturns 403 + `error.code = robots_txt_disallow` and we DO NOT fetch\nthe page. Per-target eTLD+1 throttle (60 req/min) protects the site\nfrom being scraped via subdomain rotation. `Cache-Control: no-store`\nor `private` from the target is honoured — we DO NOT write to cache\non those responses (Guardrail #4).","operationId":"brand_assets","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandAssetsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/seo/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Seo Audit Endpoint","description":"Audit a domain's homepage for SEO health and emit a 0-100 composite score.\n\n10 audit rules (each 0-10 pts): title present, title length 30-60,\nmeta description present, meta description length 50-160, exactly\none H1, canonical link, ≥3 OG tags, JSON-LD present, image alt-text\ncoverage proportional, HTTPS. `missing_signals` lists rules that did\nnot contribute so the agent has a concrete fix list.\n\nSame ethical floor as `brand_assets`: target's robots.txt is\nhonoured (Disallow `/` for our UA → 403, no fetch); per-target\neTLD+1 throttle (60 req/min) consumed BEFORE the cache lookup;\n`Cache-Control: no-store`/`private` from the target skips the\ncache write (cache_respected=false flags it).","operationId":"seo_audit","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeoAuditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/geo/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Geo Audit Endpoint","description":"Audit a domain's AI-visibility / GEO readiness and emit a 0-100 score.\n\nDeterministic + structural ONLY — no LLM is queried. 7 weighted\nrules: llms.txt present (15), AI-crawler robots access (25),\nschema.org @type coverage (20), server-side rendering (15), discovery\nsignals OG/canonical/sitemap (10), semantic headings (10), comparison\ncontent (5). `missing_signals` lists the gaps.\n\nSame ethical floor as seo_audit: target's robots.txt is honoured\n(Disallow `/` for our UA → 403, no fetch); per-target eTLD+1 throttle\n(60 req/min) consumed BEFORE the cache lookup; `Cache-Control:\nno-store`/`private` skips the cache write.","operationId":"geo_audit","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoAuditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/phone/{number}":{"get":{"tags":["Domain Intelligence"],"summary":"Phone Endpoint","description":"Phone number validation and intelligence — format, country, type, carrier, timezone.","operationId":"phone_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"string","description":"Phone number in any format (E.164 preferred, e.g. '+14155552671'; '+' URL-encoded as '%2B'). Max 50 chars. International prefix strongly recommended — without it, the country cannot be inferred.","title":"Number"},"description":"Phone number in any format (E.164 preferred, e.g. '+14155552671'; '+' URL-encoded as '%2B'). Max 50 chars. International prefix strongly recommended — without it, the country cannot be inferred."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/username/{username}":{"get":{"tags":["Domain Intelligence"],"summary":"Username Endpoint","description":"Username OSINT — check if a username exists on 16 platforms (GitHub, Reddit, X, etc.).","operationId":"username_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","description":"Username to search across platforms. Validated against [a-z0-9._-]{1,39} — lowercased server-side. Non-matching inputs return an error in the response body (not a 400) so the agent still sees the shape.","title":"Username"},"description":"Username to search across platforms. Validated against [a-z0-9._-]{1,39} — lowercased server-side. Non-matching inputs return an error in the response body (not a 400) so the agent still sees the shape."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsernameLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/whois/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Whois Endpoint","description":"WHOIS registration data for a domain.","operationId":"whois_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WhoisResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/subdomains/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Subdomains","description":"Subdomain enumeration via DNS brute force + certificate transparency.","operationId":"subdomain_enum","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubdomainsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/certs/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Certs","description":"Certificate transparency log lookup.","operationId":"ct_logs","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ssl/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Ssl Certificate","description":"SSL certificate details with grade, chain, cipher, and protocol information.","operationId":"ssl_certificate","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SslResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/threat/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Threat Intel","description":"Threat intelligence — check domain against URLhaus for known malware URLs.","operationId":"threat_intel","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/archive/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Wayback Lookup Route","description":"Web archive lookup — historical snapshots from the Wayback Machine.","operationId":"wayback_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaybackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ip/{ip}":{"get":{"tags":["Domain Intelligence"],"summary":"Ip Lookup","description":"IP intelligence — reverse DNS, ASN + country (RIPE Stat), open ports, vulnerabilities, hostnames (Shodan InternetDB), cloud provider + is_datacenter flag, Tor exit detection, severity_label, and reputation (FireHOL level1 blocklist on Free tier; +AbuseIPDB + Shodan on Pro).","operationId":"ip_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"ip","in":"path","required":true,"schema":{"type":"string","description":"IPv4 or IPv6 address, e.g. '8.8.8.8' or '2001:4860:4860::8888'. Private/reserved/loopback/link-local ranges are rejected with 400.","title":"Ip"},"description":"IPv4 or IPv6 address, e.g. '8.8.8.8' or '2001:4860:4860::8888'. Private/reserved/loopback/link-local ranges are rejected with 400."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IpLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tech/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Tech Fingerprint","description":"Technology fingerprinting — detect CMS, frameworks, servers, CDNs, analytics.","operationId":"tech_fingerprint","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TechResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/monitor/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Domain Monitor","description":"Lightweight health check — DNS up/down, SSL status, risk grade from cache. Designed for high-frequency polling.","operationId":"domain_monitor","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/domain/{domain}/vulns":{"get":{"tags":["Domain Intelligence"],"summary":"Domain Vulns","description":"Tech stack vulnerability scan — detect technologies, then look up CVEs for each.","operationId":"domain_vulns","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected.","title":"Domain"},"description":"Registrable domain name, e.g. 'example.com'. No scheme, no path, no port. Punycode (xn--*) and IDNs accepted; subdomains allowed (e.g. 'api.example.com'). Validated by validate_domain() — wildcard '*' and raw IPs rejected."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VulnsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/asn/{target}":{"get":{"tags":["Domain Intelligence"],"summary":"Asn Lookup","description":"ASN lookup — resolve target (domain or IP) to its Autonomous System Number, holder name, and announced prefixes.","operationId":"asn_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"target","in":"path","required":true,"schema":{"type":"string","description":"ASN or IP. Accepts 'AS13335', '13335', or an IPv4/IPv6 address. For IP input, the response resolves the containing ASN via RIPE Stat.","title":"Target"},"description":"ASN or IP. Accepts 'AS13335', '13335', or an IPv4/IPv6 address. For IP input, the response resolves the containing ASN via RIPE Stat."},{"name":"include_full_prefixes","in":"query","required":false,"schema":{"type":"boolean","description":"Return the full announced-prefixes list (default: false, returns first 50). ipv4_count and ipv6_count are always honest pre-truncation totals. Set true for network mapping or BGP route audits.","default":false,"title":"Include Full Prefixes"},"description":"Return the full announced-prefixes list (default: false, returns first 50). ipv4_count and ipv6_count are always honest pre-truncation totals. Set true for network mapping or BGP route audits."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/domains/bulk":{"post":{"tags":["Domain Intelligence"],"summary":"Bulk Domain Report","description":"Bulk domain intelligence — up to 50 domains per call (Pydantic input cap). Each domain\nconsumes 1 unit of the per-hour quota; entries beyond the caller's remaining quota land in\n`skipped_due_to_rate_limit` instead of failing the whole batch (v1.27 dynamic budget).","operationId":"bulk_domain_report","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BulkRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/audit/{domain}":{"get":{"tags":["Domain Intelligence"],"summary":"Audit Domain","description":"Comprehensive domain audit — full intelligence report + technology fingerprint + live HTTP headers in a single call.\n\nAggregates DNS, SSL, WHOIS, subdomains, threat intelligence, technology detection,\nHTTP security headers, and reputation data. Designed for AI agents and security\nautomation that need a complete picture in one request.","operationId":"audit_domain","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"include_all_txt","in":"query","required":false,"schema":{"type":"boolean","description":"Return every TXT record under report.dns.txt (default: only SPF, DMARC, DKIM, MTA-STS, TLS-RPT). report.dns.total_txt_records is always emitted with the honest pre-filter count. Mirrors /v1/domain/{domain}'s include_all_txt — default keeps the audit response slim by stripping vendor verification strings.","default":false,"title":"Include All Txt"},"description":"Return every TXT record under report.dns.txt (default: only SPF, DMARC, DKIM, MTA-STS, TLS-RPT). report.dns.total_txt_records is always emitted with the honest pre-filter count. Mirrors /v1/domain/{domain}'s include_all_txt — default keeps the audit response slim by stripping vendor verification strings."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/threat-report/{ip}":{"get":{"tags":["Domain Intelligence"],"summary":"Threat Report","description":"Comprehensive IP threat report — Shodan InternetDB + AbuseIPDB + Shodan full + ASN in a single call.\n\nAggregates open ports, vulnerabilities, abuse reports, geolocation, ASN ownership,\nand reputation across multiple sources. Designed for SOC triage and threat hunting\nwhere a complete IP profile is needed without making 4+ separate API calls.","operationId":"threat_report","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"ip","in":"path","required":true,"schema":{"type":"string","description":"IPv4 or IPv6 address, e.g. '8.8.8.8' or '2001:4860:4860::8888'. Private/reserved/loopback/link-local ranges are rejected with 400.","title":"Ip"},"description":"IPv4 or IPv6 address, e.g. '8.8.8.8' or '2001:4860:4860::8888'. Private/reserved/loopback/link-local ranges are rejected with 400."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreatReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cve/leading":{"get":{"tags":["CVE Intelligence"],"summary":"Cve Leading","description":"CVEs indexed from MITRE/GHSA before NVD has enriched them. These are\nvulnerabilities we know about that NVD hasn't published yet — our unique\nearly-warning feed.","operationId":"cve_leading","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results per page","default":50,"title":"Limit"},"description":"Max results per page"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":0,"description":"Number of results to skip (for pagination)","default":0,"title":"Offset"},"description":"Number of results to skip (for pagination)"},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Per-result detail level. Default returns slim list items (cve_id, summary, severity, cvss_v3, cwe_id, epss, kev, total_products, published, modified, sources, verdict). Pass include=full to also return description, cvss_breakdown, affected_products, references, first_seen_source, first_seen_at. Slim default avoids the description/summary duplication that bloats 50-item leading lists; for drill-down prefer cve_lookup.","title":"Include"},"description":"Per-result detail level. Default returns slim list items (cve_id, summary, severity, cvss_v3, cwe_id, epss, kev, total_products, published, modified, sources, verdict). Pass include=full to also return description, cvss_breakdown, affected_products, references, first_seen_source, first_seen_at. Slim default avoids the description/summary duplication that bloats 50-item leading lists; for drill-down prefer cve_lookup."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CveSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cve/{cve_id}":{"get":{"tags":["CVE Intelligence"],"summary":"Cve Lookup","description":"Look up a single CVE by ID. Returns full details with EPSS score and KEV status.","operationId":"cve_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"cve_id","in":"path","required":true,"schema":{"type":"string","description":"CVE identifier in canonical form 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Examples: 'CVE-2021-44228', 'CVE-2014-0160'.","title":"Cve Id"},"description":"CVE identifier in canonical form 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Examples: 'CVE-2021-44228', 'CVE-2014-0160'."},{"name":"include_affected_products","in":"query","required":false,"schema":{"type":"boolean","description":"Return full affected_products list (default: first 20). Use for bulk audits or dependency scans.","default":false,"title":"Include Affected Products"},"description":"Return full affected_products list (default: first 20). Use for bulk audits or dependency scans."},{"name":"include_full_references","in":"query","required":false,"schema":{"type":"boolean","description":"Return full references list (default: first 10). total_references is always emitted with the honest count. Patch URL detection always runs against the full list, so patch_url/patch_available are unaffected by the cap.","default":false,"title":"Include Full References"},"description":"Return full references list (default: first 10). total_references is always emitted with the honest count. Patch URL detection always runs against the full list, so patch_url/patch_available are unaffected by the cap."},{"name":"include_reference_tags","in":"query","required":false,"schema":{"type":"boolean","description":"Return structured `references_full` field with [{url, tags, source}] objects (NVD reference tags + source provenance). Default False keeps `references` as plain URL list (backward compat). Combine with include_full_references=true for full untruncated structured list.","default":false,"title":"Include Reference Tags"},"description":"Return structured `references_full` field with [{url, tags, source}] objects (NVD reference tags + source provenance). Default False keeps `references` as plain URL list (backward compat). Combine with include_full_references=true for full untruncated structured list."},{"name":"include_severity_breakdown","in":"query","required":false,"schema":{"type":"boolean","description":"Return severity_sources/severity_consensus/severity_disagreement (multi-source severity breakdown). Default False keeps response shape backward-compat. Set True to inspect vendor disputes (e.g. CVE-2023-38545 NVD-CRITICAL vs GHSA-HIGH). cvss_v2 / cvss_v2_vector are always emitted (additive non-opt-in).","default":false,"title":"Include Severity Breakdown"},"description":"Return severity_sources/severity_consensus/severity_disagreement (multi-source severity breakdown). Default False keeps response shape backward-compat. Set True to inspect vendor disputes (e.g. CVE-2023-38545 NVD-CRITICAL vs GHSA-HIGH). cvss_v2 / cvss_v2_vector are always emitted (additive non-opt-in)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cve/{cve_id}/risk_score":{"get":{"tags":["CVE Intelligence"],"summary":"Calculate Risk Score","description":"Composite CVE risk score (0-100) — fuses CVSS, EPSS, KEV, and PoC signals.\n\nFormula: CVSS*0.20 + EPSS*0.35 + KEV*0.30 + PoC*0.15 (each component\nrescaled to 0-100 before weighting), with multiplicative boosters for\nKEV+PoC combo, critical-severity high-EPSS, and recent publication.\nReturns score, label (CRITICAL/HIGH/MEDIUM/LOW), urgency, and a\none-sentence remediation hint — agent-friendly triage signal in a\nsingle call.\n\nPoC signal here is the local ExploitDB mirror only. For full\nmulti-source exploit detail (GitHub Advisory + Shodan refs +\nExploitDB), call exploit_lookup separately.\n\nMethodology adapted from mukul975/cve-mcp-server (Apache-2.0):\nhttps://github.com/mukul975/cve-mcp-server.","operationId":"calculate_risk_score","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"cve_id","in":"path","required":true,"schema":{"type":"string","description":"CVE identifier 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Example: 'CVE-2021-44228'.","title":"Cve Id"},"description":"CVE identifier 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Example: 'CVE-2021-44228'."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskScoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cvss/details":{"get":{"tags":["CVE Intelligence"],"summary":"Get Cvss Details","description":"Parse a CVSS v3.x vector string into per-metric breakdown + recomputed base score.\n\nUse this to translate a raw vector (e.g. from a CVE record) into a\nhuman-readable / agent-friendly structure: attack_vector, attack_complexity,\nprivileges_required, user_interaction, scope, and the three impact metrics\n(C/I/A). Also re-derives base_score and base_severity from the vector so\ncallers can verify upstream NVD scoring.","operationId":"get_cvss_details","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"vector","in":"query","required":true,"schema":{"type":"string","maxLength":500,"description":"CVSS v3.0 or v3.1 vector string, e.g. 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H'. Real vectors are ~80-150 chars; the 500-char cap is a defensive ceiling.","title":"Vector"},"description":"CVSS v3.0 or v3.1 vector string, e.g. 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H'. Real vectors are ~80-150 chars; the 500-char cap is a defensive ceiling."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CvssDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/kev/{cve_id}":{"get":{"tags":["CVE Intelligence"],"summary":"Kev Detail","description":"Look up CISA KEV (Known Exploited Vulnerabilities) full record for a CVE.\n\nReturns federal patch deadline (due_date), CISA-specified remediation\n(required_action), known ransomware association, vendor/product, common\nvulnerability name (e.g. 'Log4Shell'), and CISA-reported CWE list. 404 when\nthe CVE is not in the KEV catalog; use cve_lookup for non-KEV CVEs.","operationId":"kev_detail","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"cve_id","in":"path","required":true,"schema":{"type":"string","description":"CVE identifier in canonical form 'CVE-YYYY-NNNN+' (case-insensitive; normalized server-side). Returns 404 when the CVE is not in the CISA KEV catalog — use cve_lookup for non-KEV CVEs.","title":"Cve Id"},"description":"CVE identifier in canonical form 'CVE-YYYY-NNNN+' (case-insensitive; normalized server-side). Returns 404 when the CVE is not in the CISA KEV catalog — use cve_lookup for non-KEV CVEs."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KevDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cwe/{cwe_id}":{"get":{"tags":["CVE Intelligence"],"summary":"Cwe Lookup","description":"Look up a MITRE CWE (Common Weakness Enumeration) catalog record.\n\nReturns description, abstract type, status, likelihood of exploit, recommended\nmitigations, observed example CVEs, and parent/child weakness chain. Use this\nafter cve_lookup or kev_detail to understand the underlying weakness category.","operationId":"cwe_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"cwe_id","in":"path","required":true,"schema":{"type":"string","description":"CWE identifier in canonical form 'CWE-<digits>'. Tolerant of 'cwe-79', 'CWE 79', or bare '79'. Returns 404 when the CWE is not in MITRE's research view 1000.","title":"Cwe Id"},"description":"CWE identifier in canonical form 'CWE-<digits>'. Tolerant of 'cwe-79', 'CWE 79', or bare '79'. Returns 404 when the CWE is not in MITRE's research view 1000."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim record (first 3 mitigations, first 3 examples, no extended_description). total_mitigations / total_examples are always honest pre-truncation counts. Pass include=full to restore extended_description and the full mitigations + examples lists.","title":"Include"},"description":"Detail level. Default returns slim record (first 3 mitigations, first 3 examples, no extended_description). total_mitigations / total_examples are always honest pre-truncation counts. Pass include=full to restore extended_description and the full mitigations + examples lists."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CweLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cves":{"get":{"tags":["CVE Intelligence"],"summary":"Cve Search","description":"Search CVEs by product, severity, date range, KEV status, and EPSS score.","operationId":"cve_search","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"product","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Filter by product/vendor name. Exact match (case-insensitive) against NVD CPE tokens — not substring. Use canonical names: 'nginx', 'apache', 'linux_kernel'.","title":"Product"},"description":"Filter by product/vendor name. Exact match (case-insensitive) against NVD CPE tokens — not substring. Use canonical names: 'nginx', 'apache', 'linux_kernel'."},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by severity: CRITICAL, HIGH, MEDIUM, LOW","title":"Severity"},"description":"Filter by severity: CRITICAL, HIGH, MEDIUM, LOW"},{"name":"published_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Inclusive lower bound on publish date (YYYY-MM-DD, UTC). Example: 2015-01-01 returns CVEs published on or after that day.","title":"Published After"},"description":"Inclusive lower bound on publish date (YYYY-MM-DD, UTC). Example: 2015-01-01 returns CVEs published on or after that day."},{"name":"published_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Inclusive upper bound on publish date (YYYY-MM-DD, UTC). Example: 2020-12-31 returns CVEs published on or before that day.","title":"Published Before"},"description":"Inclusive upper bound on publish date (YYYY-MM-DD, UTC). Example: 2020-12-31 returns CVEs published on or before that day."},{"name":"kev","in":"query","required":false,"schema":{"type":"boolean","description":"Filter to CISA KEV entries only","default":false,"title":"Kev"},"description":"Filter to CISA KEV entries only"},{"name":"epss_min","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"description":"Minimum EPSS score (0.0-1.0)","title":"Epss Min"},"description":"Minimum EPSS score (0.0-1.0)"},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort order: epss_desc, cvss_desc, published_desc (default)","title":"Sort"},"description":"Sort order: epss_desc, cvss_desc, published_desc (default)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results per page","default":50,"title":"Limit"},"description":"Max results per page"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":0,"description":"Number of results to skip (for pagination)","default":0,"title":"Offset"},"description":"Number of results to skip (for pagination)"},{"name":"cwe_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by CWE ID (e.g. CWE-79, CWE-89, CWE-120)","title":"Cwe Id"},"description":"Filter by CWE ID (e.g. CWE-79, CWE-89, CWE-120)"},{"name":"cvss_min","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":10.0,"minimum":0.0},{"type":"null"}],"description":"Minimum CVSS v3 score (0.0-10.0). CVEs with null CVSS are excluded.","title":"Cvss Min"},"description":"Minimum CVSS v3 score (0.0-10.0). CVEs with null CVSS are excluded."},{"name":"cvss_max","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":10.0,"minimum":0.0},{"type":"null"}],"description":"Maximum CVSS v3 score (0.0-10.0). CVEs with null CVSS are excluded.","title":"Cvss Max"},"description":"Maximum CVSS v3 score (0.0-10.0). CVEs with null CVSS are excluded."},{"name":"vendor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Filter by vendor name (case-insensitive). When combined with product, both must match the same cpe row.","title":"Vendor"},"description":"Filter by vendor name (case-insensitive). When combined with product, both must match the same cpe row."},{"name":"tagged","in":"query","required":false,"schema":{"type":"boolean","description":"Default False — only return CVEs where the queried product is the actually-vulnerable component (NVD CPE vulnerable=true). Set True to broaden: include CVEs where the product appears as a target dependency (target_hw / target_sw) — e.g. product=linux_kernel with tagged=true also returns CVEs in apps that run on Linux. Rows synced before v1.30.0 have vulnerable=NULL and are treated as vulnerable=true for back-compat.","default":false,"title":"Tagged"},"description":"Default False — only return CVEs where the queried product is the actually-vulnerable component (NVD CPE vulnerable=true). Set True to broaden: include CVEs where the product appears as a target dependency (target_hw / target_sw) — e.g. product=linux_kernel with tagged=true also returns CVEs in apps that run on Linux. Rows synced before v1.30.0 have vulnerable=NULL and are treated as vulnerable=true for back-compat."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Per-result detail level. Default returns slim list items (cve_id, summary, severity, cvss_v3, cwe_id, epss, kev, total_products, published, modified, sources, verdict). Pass include=full to also return description, cvss_breakdown, affected_products, references, first_seen_source, first_seen_at. Slim default keeps token cost low when agents are filtering or paginating; for drill-down on a single CVE prefer cve_lookup.","title":"Include"},"description":"Per-result detail level. Default returns slim list items (cve_id, summary, severity, cvss_v3, cwe_id, epss, kev, total_products, published, modified, sources, verdict). Pass include=full to also return description, cvss_breakdown, affected_products, references, first_seen_source, first_seen_at. Slim default keeps token cost low when agents are filtering or paginating; for drill-down on a single CVE prefer cve_lookup."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CveSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/exploit/{cve_id}":{"get":{"tags":["CVE Intelligence"],"summary":"Exploit Lookup","description":"Search for public exploits and advisories related to a CVE.","operationId":"exploit_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"cve_id","in":"path","required":true,"schema":{"type":"string","description":"CVE identifier 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Example: 'CVE-2021-44228'.","title":"Cve Id"},"description":"CVE identifier 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case server-side). Example: 'CVE-2021-44228'."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExploitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cves/bulk":{"post":{"tags":["CVE Intelligence"],"summary":"Bulk Cve Lookup","description":"Bulk CVE lookup — up to 50 CVEs per call (Pydantic input cap). Each CVE consumes 1 unit\nof the per-hour quota; ids beyond the caller's remaining quota land in\n`skipped_due_to_rate_limit` instead of failing the whole batch (v1.27 dynamic budget).","operationId":"bulk_cve_lookup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BulkCveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/check/secrets":{"post":{"tags":["Code Security"],"summary":"Check Secrets Endpoint","description":"Detect hardcoded secrets (AWS keys, tokens, passwords, etc.) in source code.","operationId":"check_secrets","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/check/injection":{"post":{"tags":["Code Security"],"summary":"Check Injection Endpoint","description":"Detect SQL injection, command injection, and path traversal patterns in source code.","operationId":"check_injection","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/scan/headers/{domain}":{"get":{"tags":["Code Security","Domain Intelligence"],"summary":"Scan Headers Endpoint","description":"Fetch a domain's HTTP headers live and analyze security posture.","operationId":"scan_headers","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain, e.g. 'example.com'. No scheme, no path. Bare IPs are rejected — use /v1/ip/{ip} instead. Live HTTPS fetch is performed; use /v1/check/headers (POST) to analyze a header dict you already have.","title":"Domain"},"description":"Registrable domain, e.g. 'example.com'. No scheme, no path. Bare IPs are rejected — use /v1/ip/{ip} instead. Live HTTPS fetch is performed; use /v1/check/headers (POST) to analyze a header dict you already have."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim findings (raw header values capped at 500 chars; total_value_length carries the honest pre-truncation length when truncation occurred). Pass include=full to restore the full raw value for every present-with-validator header (useful for inspecting full CSP directives end-to-end).","title":"Include"},"description":"Detail level. Default returns slim findings (raw header values capped at 500 chars; total_value_length carries the honest pre-truncation length when truncation occurred). Pass include=full to restore the full raw value for every present-with-validator header (useful for inspecting full CSP directives end-to-end)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanHeadersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/check/headers":{"post":{"tags":["Code Security"],"summary":"Check Headers Endpoint","description":"Validate HTTP security headers (CSP, HSTS, X-Frame-Options, etc.).","operationId":"check_headers","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim findings (raw header values capped at 500 chars; total_value_length carries the honest pre-truncation length when truncation occurred). Pass include=full to restore the full raw value for every present-with-validator header.","title":"Include"},"description":"Detail level. Default returns slim findings (raw header values capped at 500 chars; total_value_length carries the honest pre-truncation length when truncation occurred). Pass include=full to restore the full raw value for every present-with-validator header."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeadersInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckHeadersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/check/dependencies":{"post":{"tags":["Code Security"],"summary":"Check Dependencies Endpoint","description":"Check packages against the CVE database for known vulnerabilities.\n\nUp to 50 packages per call (Pydantic input cap). Each package consumes 1 unit of the per-hour\nquota; entries beyond the caller's remaining quota land in `skipped_due_to_rate_limit`\ninstead of failing the whole batch (v1.27 dynamic budget).","operationId":"check_dependencies","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependenciesInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependenciesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/ioc/{indicator}":{"get":{"tags":["Threat Intelligence"],"summary":"Ioc Lookup","description":"Unified IOC enrichment — auto-detects type and queries abuse.ch feeds.\n\nSource coverage by type: hash → ThreatFox only; IP → ThreatFox + Feodo + URLhaus;\ndomain / URL → ThreatFox + URLhaus. Feodo and URLhaus do not index hashes.","operationId":"ioc_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"indicator","in":"path","required":true,"schema":{"type":"string","description":"Indicator of compromise — auto-detected type. Accepts: IP (IPv4/IPv6), domain, URL (with scheme), or file hash (MD5/SHA1/SHA256/SHA512, hex). Max 2048 chars.","title":"Indicator"},"description":"Indicator of compromise — auto-detected type. Accepts: IP (IPv4/IPv6), domain, URL (with scheme), or file hash (MD5/SHA1/SHA256/SHA512, hex). Max 2048 chars."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/hash/{file_hash}":{"get":{"tags":["Threat Intelligence"],"summary":"Hash Lookup","description":"Malware file hash reputation lookup via MalwareBazaar.","operationId":"hash_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"file_hash","in":"path","required":true,"schema":{"type":"string","description":"File hash (hex, case-insensitive). Accepted lengths: MD5=32, SHA1=40, SHA256=64. Other lengths or non-hex characters return 400.","title":"File Hash"},"description":"File hash (hex, case-insensitive). Accepted lengths: MD5=32, SHA1=40, SHA256=64. Other lengths or non-hex characters return 400."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HashResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/password/{sha1_hash}":{"get":{"tags":["Threat Intelligence"],"summary":"Password Check","description":"Password breach check via HIBP Pwned Passwords (k-anonymity). Send full SHA1 hash, get found + breach count.","operationId":"password_check","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"sha1_hash","in":"path","required":true,"schema":{"type":"string","description":"Full SHA-1 hash of the password (40 hex chars, case-insensitive). k-anonymity is applied server-side: only the first 5 chars are sent to HIBP.","title":"Sha1 Hash"},"description":"Full SHA-1 hash of the password (40 hex chars, case-insensitive). k-anonymity is applied server-side: only the first 5 chars are sent to HIBP."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/phishing/{url}":{"get":{"tags":["Threat Intelligence"],"summary":"Phishing Check","description":"Check if a URL is malicious via URLhaus (host + exact URL lookup).","operationId":"phishing_check","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"url","in":"path","required":true,"schema":{"type":"string","description":"Full URL to check (must include scheme, e.g. 'https://example.com/path'). URL-encode any '?' or '#' chars the agent wants preserved into the path component. Checked against URLhaus for both exact URL and host-level matches.","title":"Url"},"description":"Full URL to check (must include scheme, e.g. 'https://example.com/path'). URL-encode any '?' or '#' chars the agent wants preserved into the path component. Checked against URLhaus for both exact URL and host-level matches."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhishingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/iocs/bulk":{"post":{"tags":["Threat Intelligence"],"summary":"Bulk Ioc Lookup","description":"Bulk IOC enrichment — up to 50 indicators per call (Pydantic input cap). Each indicator\nconsumes 1 unit of the per-hour quota; entries beyond the caller's remaining quota land in\n`skipped_due_to_rate_limit` instead of failing the whole batch (v1.27 dynamic budget).","operationId":"bulk_ioc_lookup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BulkIocRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkIocResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/atlas/techniques":{"get":{"tags":["MITRE ATLAS"],"summary":"Atlas Technique Search","description":"Search the MITRE ATLAS technique catalog by keyword, tactic, or maturity.\n\nUse this to discover AI/ML attack techniques relevant to a given threat\nmodel. Drill into atlas_technique_lookup with the returned technique_id for\nfull description, ATT&CK bridge, and next_calls pivot hints.","operationId":"atlas_technique_search","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"keyword","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Substring match against technique name + description (case-insensitive).","title":"Keyword"},"description":"Substring match against technique name + description (case-insensitive)."},{"name":"tactic","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by ATLAS tactic id, e.g. 'AML.TA0002' (Reconnaissance). Format 'AML.TA####'.","title":"Tactic"},"description":"Filter by ATLAS tactic id, e.g. 'AML.TA0002' (Reconnaissance). Format 'AML.TA####'."},{"name":"maturity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by maturity: 'demonstrated' (observed in real attacks) or 'feasible' (theoretical).","title":"Maturity"},"description":"Filter by maturity: 'demonstrated' (observed in real attacks) or 'feasible' (theoretical)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results to return.","default":50,"title":"Limit"},"description":"Max results to return."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim records (description truncated to 240 chars; drill via atlas_technique_lookup for full text). Pass include=full for the verbose description on every row — large catalogs (167 techniques) can return ~100KB at full.","title":"Include"},"description":"Detail level. Default returns slim records (description truncated to 240 chars; drill via atlas_technique_lookup for full text). Pass include=full for the verbose description on every row — large catalogs (167 techniques) can return ~100KB at full."},{"name":"exclude_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional ATLAS technique id to exclude from the results, format 'AML.T####' or 'AML.T####.###'. Useful when paired with a tactic filter to fetch siblings without the originating technique itself (e.g. when atlas_technique_lookup's next_calls hint leads here).","title":"Exclude Id"},"description":"Optional ATLAS technique id to exclude from the results, format 'AML.T####' or 'AML.T####.###'. Useful when paired with a tactic filter to fetch siblings without the originating technique itself (e.g. when atlas_technique_lookup's next_calls hint leads here)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AtlasTechniqueSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/atlas/case-studies":{"get":{"tags":["MITRE ATLAS"],"summary":"Atlas Case Study Search","description":"Search ATLAS case studies by keyword or referenced technique.\n\nUseful when you've already identified a technique and want to see real-world\nincidents that exercised it. Returns slim records; drill via\natlas_case_study_lookup for the full procedure list.","operationId":"atlas_case_study_search","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"keyword","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Substring match against case study name + description (case-insensitive).","title":"Keyword"},"description":"Substring match against case study name + description (case-insensitive)."},{"name":"technique_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to case studies that include this ATLAS technique id, e.g. 'AML.T0000'.","title":"Technique Id"},"description":"Filter to case studies that include this ATLAS technique id, e.g. 'AML.T0000'."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results to return.","default":50,"title":"Limit"},"description":"Max results to return."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim records (description truncated to 240 chars). Pass include=full for the verbose summary on every row.","title":"Include"},"description":"Detail level. Default returns slim records (description truncated to 240 chars). Pass include=full for the verbose summary on every row."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AtlasCaseStudySearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/atlas/case-studies/{case_study_id}":{"get":{"tags":["MITRE ATLAS"],"summary":"Atlas Case Study Lookup","description":"Look up a MITRE ATLAS case study — a real-world AI/ML attack incident.\n\nEach case study links a sequence of ATLAS techniques (techniques_used) to a\ndocumented incident. Use atlas_technique_lookup on each id (or\nbulk_atlas_technique_lookup for the whole list) to expand into\ntechnique-level detail.\n\nDefault response is SLIM (description truncated). Pass include=full for\nthe verbose narrative.","operationId":"atlas_case_study_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"case_study_id","in":"path","required":true,"schema":{"type":"string","description":"Canonical ATLAS case study id matching 'AML.CS####', e.g. 'AML.CS0000'. Returns 404 when the id is not in the synced catalog.","title":"Case Study Id"},"description":"Canonical ATLAS case study id matching 'AML.CS####', e.g. 'AML.CS0000'. Returns 404 when the id is not in the synced catalog."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim (description truncated to 240 chars). Pass include=full for the verbose incident summary; case-study descriptions can run 1-3KB.","title":"Include"},"description":"Detail level. Default returns slim (description truncated to 240 chars). Pass include=full for the verbose incident summary; case-study descriptions can run 1-3KB."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AtlasCaseStudyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/atlas/techniques/bulk":{"post":{"tags":["MITRE ATLAS"],"summary":"Bulk Atlas Technique Lookup","description":"Bulk ATLAS technique lookup — up to 50 technique ids in one call (Pydantic input cap).\n\nDesigned as the natural follow-up to atlas_case_study_lookup (which carries\na list of techniques_used) — drill into all techniques in a single request\ninstead of N separate atlas_technique_lookup calls. Each entry's record is\nthe same shape as /v1/atlas/{technique_id}, including parent-tactics\ninheritance for sub-techniques (inherited_tactics flag set when applicable).\n\nv1.27 dynamic budget: each id consumes 1 unit of the per-hour quota; ids\nbeyond the caller's remaining quota land in `skipped_due_to_rate_limit`\ninstead of failing the whole request.","operationId":"bulk_atlas_technique_lookup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BulkAtlasTechniqueRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkAtlasTechniqueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/atlas/{technique_id}":{"get":{"tags":["MITRE ATLAS"],"summary":"Atlas Technique Lookup","description":"Look up a MITRE ATLAS technique (AI/ML attack catalog).\n\nATLAS catalogues adversarial techniques targeting AI/ML systems — LLM prompt\ninjection, model evasion, training data poisoning, and similar TTPs. About\n20% of ATLAS techniques bridge to ATT&CK via attack_reference_id; use that\nto pivot to D3FEND defenses through d3fend_defense_for_attack.","operationId":"atlas_technique_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"technique_id","in":"path","required":true,"schema":{"type":"string","description":"Canonical ATLAS technique id matching 'AML.T####' or 'AML.T####.###' (sub-technique), e.g. 'AML.T0000', 'AML.T0000.000'. Returns 404 when the id is not in the synced ATLAS catalog.","title":"Technique Id"},"description":"Canonical ATLAS technique id matching 'AML.T####' or 'AML.T####.###' (sub-technique), e.g. 'AML.T0000', 'AML.T0000.000'. Returns 404 when the id is not in the synced ATLAS catalog."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AtlasTechniqueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/d3fend/defenses":{"get":{"tags":["MITRE D3FEND"],"summary":"D3Fend Defense Search","description":"Search MITRE D3FEND defenses by keyword, tactic, or targeted artifact.\n\nUse this to discover defensive techniques relevant to a threat model. Drill\nvia d3fend_defense_lookup with the returned defense_id for the full record\n+ the list of ATT&CK T-codes the defense mitigates.\n\nDefault response is SLIM (drops `uri` from each row). Pass `include=full`\nfor the verbose record on every row.","operationId":"d3fend_defense_search","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"keyword","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Substring match against defense label, description, or parent_label (case-insensitive).","title":"Keyword"},"description":"Substring match against defense label, description, or parent_label (case-insensitive)."},{"name":"tactic","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by D3FEND tactic. One of: Model, Harden, Detect, Isolate, Deceive, Evict, Restore.","title":"Tactic"},"description":"Filter by D3FEND tactic. One of: Model, Harden, Detect, Isolate, Deceive, Evict, Restore."},{"name":"artifact","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":100},{"type":"null"}],"description":"Filter by exact digital artifact targeted by the defense, e.g. 'Access Token', 'File'.","title":"Artifact"},"description":"Filter by exact digital artifact targeted by the defense, e.g. 'Access Token', 'File'."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results to return.","default":50,"title":"Limit"},"description":"Max results to return."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim rows (drops the deterministic ontology `uri` field — saves ~60 chars/row, ~30% on popular T-code drills). Pass include=full to get the `uri` back. The slug `defense_id` is always returned and uniquely identifies the defense; `uri` is reconstructible from it.","title":"Include"},"description":"Detail level. Default returns slim rows (drops the deterministic ontology `uri` field — saves ~60 chars/row, ~30% on popular T-code drills). Pass include=full to get the `uri` back. The slug `defense_id` is always returned and uniquely identifies the defense; `uri` is reconstructible from it."},{"name":"exclude_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional D3FEND defense slug to exclude from results (CamelCase, e.g. 'TokenBinding'). Useful when paired with an artifact filter to fetch siblings without the originating defense itself (chained from d3fend_defense_lookup's next_calls).","title":"Exclude Id"},"description":"Optional D3FEND defense slug to exclude from results (CamelCase, e.g. 'TokenBinding'). Useful when paired with an artifact filter to fetch siblings without the originating defense itself (chained from d3fend_defense_lookup's next_calls)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/D3fendDefenseSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/d3fend/attack/{attack_technique_id}":{"get":{"tags":["MITRE D3FEND"],"summary":"D3Fend Defense For Attack","description":"Reverse lookup: given an ATT&CK T-code, list every D3FEND defense that mitigates it.\n\nThis is the bridge from offensive intelligence (ATT&CK / ATLAS / CVE) to\ndefensive playbook. Pair with cve_lookup or atlas_technique_lookup output —\nwhen those carry an ATT&CK id, call this tool to surface the mitigations.\nReturns 200 with empty defenses on no match (the gap itself is signal).\n\n`defenses` is capped at `limit` (default 30) for token efficiency; `total`\nis the honest count and `coverage_by_tactic` aggregates ALL matching\ndefenses, not just the truncated slice. `next_calls` emits a single\ndrill hint into the top defense via d3fend_defense_lookup; empty defense\nlist emits no pivot (the gap is the signal).\n\nDefault response is SLIM (drops `uri` from each row). Pass `include=full`\nfor the verbose record.","operationId":"d3fend_defense_for_attack","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"attack_technique_id","in":"path","required":true,"schema":{"type":"string","description":"ATT&CK technique id matching 'T####' or 'T####.###' (e.g. 'T1059', 'T1550.001'). Returns 200 with empty defenses list when the T-code has no D3FEND mapping.","title":"Attack Technique Id"},"description":"ATT&CK technique id matching 'T####' or 'T####.###' (e.g. 'T1059', 'T1550.001'). Returns 200 with empty defenses list when the T-code has no D3FEND mapping."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Cap on `defenses` array length. Default 30; popular T-codes (e.g. T1059, T1078) map to 30-50+ D3FEND techniques. `total` and `coverage_by_tactic` always reflect the honest pre-truncation counts.","default":30,"title":"Limit"},"description":"Cap on `defenses` array length. Default 30; popular T-codes (e.g. T1059, T1078) map to 30-50+ D3FEND techniques. `total` and `coverage_by_tactic` always reflect the honest pre-truncation counts."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Detail level. Default returns slim rows (drops the deterministic ontology `uri` field — popular T-codes with 15+ defenses save ~900 chars). Pass include=full to get `uri` back on every row. The slug `defense_id` is always returned.","title":"Include"},"description":"Detail level. Default returns slim rows (drops the deterministic ontology `uri` field — popular T-codes with 15+ defenses save ~900 chars). Pass include=full to get `uri` back on every row. The slug `defense_id` is always returned."},{"name":"exclude_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional D3FEND defense slug to exclude from results. Used by chain pivots from d3fend_defense_lookup so the agent does not see itself in the 'see also' list.","title":"Exclude Id"},"description":"Optional D3FEND defense slug to exclude from results. Used by chain pivots from d3fend_defense_lookup so the agent does not see itself in the 'see also' list."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/D3fendForAttackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/d3fend/coverage":{"post":{"tags":["MITRE D3FEND"],"summary":"D3Fend Attack Coverage","description":"Batch coverage breakdown: given a list of ATT&CK T-codes, return defense counts per tactic + identify undefended techniques.\n\nUse this to assess the defensive posture of an entire campaign or threat\nmodel in one call. Defended_techniques is the subset that has at least one\nD3FEND mapping; undefended_techniques are the gaps. coverage_by_tactic\ncounts DISTINCT defenses per D3FEND tactic across the whole input set.","operationId":"d3fend_attack_coverage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/D3fendCoverageBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/D3fendCoverageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/d3fend/{defense_id}":{"get":{"tags":["MITRE D3FEND"],"summary":"D3Fend Defense Lookup","description":"Look up a MITRE D3FEND defense technique by slug.\n\nReturns the defense's tactic (one of 7 D3FEND tactics), targeted digital\nartifact, and the list of ATT&CK T-codes it mitigates (attack_techniques).\nUse this after d3fend_defense_search or as a follow-up to\nd3fend_defense_for_attack to inspect a specific defense in detail.","operationId":"d3fend_defense_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"defense_id","in":"path","required":true,"schema":{"type":"string","description":"D3FEND defense slug (CamelCase from the ontology URI fragment), e.g. 'TokenBinding', 'FileHashing'. Returns 404 when not in catalog.","title":"Defense Id"},"description":"D3FEND defense slug (CamelCase from the ontology URI fragment), e.g. 'TokenBinding', 'FileHashing'. Returns 404 when not in catalog."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/D3fendDefenseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/scan/{domain}":{"get":{"tags":["Website Scanner"],"summary":"Contrast Scan Endpoint","description":"Active website security scan — C scanner engine (11 modules) + severity-ranked findings.\n\nRuns HTTP security headers, SSL/TLS, DNS, redirect-chain, information\ndisclosure, cookie-flags, DNSSEC, HTTP-methods, CORS, HTML, and deep-CSP\nchecks against the live site and returns a letter grade plus enriched,\nseverity-sorted findings. Performs active outbound requests — a per-target\neTLD+1 throttle (60 req/min) applies on top of the caller's rate limit.","operationId":"contrast_scan","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"Registrable domain, e.g. 'example.com'. Scheme/path/port are stripped. Bare IPs and private-resolving domains are rejected (SSRF defense).","title":"Domain"},"description":"Registrable domain, e.g. 'example.com'. Scheme/path/port are stripped. Bare IPs and private-resolving domains are rejected (SSRF defense)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/status":{"get":{"tags":["Meta"],"summary":"Api Status","description":"API health check and data freshness.","operationId":"api_status","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/usage":{"get":{"tags":["Meta"],"summary":"Api Usage","description":"Usage statistics for API key holders.","operationId":"api_usage","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/privacy/my-data":{"get":{"tags":["Meta"],"summary":"Privacy My Data","description":"Return everything this API has stored about you. GDPR-style transparency.\n\nShows the hashed IP, Pro key record (if any), and last-24h endpoint usage.\nThe raw domains, IPs, CVEs, hashes, or code you submitted are NEVER stored —\npath parameters are stripped before any DB write (see db.normalize_endpoint).","operationId":"privacy_my_data","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"ContrastAPIKey":[]}]}},"/v1/capabilities":{"get":{"tags":["Meta"],"summary":"Api Capabilities","description":"Machine-readable catalog of all MCP tools and REST endpoints.","operationId":"api_capabilities","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/sigma/search":{"get":{"tags":["Sigma Rules"],"summary":"Sigma Search","description":"Multi-param search across the Sigma rule corpus.\n\nFilters compose with AND logic. When no filter is supplied, all non-deprecated rules are returned.","operationId":"sigma_rule_search","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"technique","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Technique"}},{"name":"cve_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Cve Id"}},{"name":"logsource_product","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Logsource Product"}},{"name":"logsource_category","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Logsource Category"}},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Query"}},{"name":"status","in":"query","required":false,"schema":{"enum":["all","test","stable","experimental","unsupported","deprecated"],"type":"string","default":"all","title":"Status"}},{"name":"level","in":"query","required":false,"schema":{"enum":["all","informational","low","medium","high","critical"],"type":"string","default":"all","title":"Level"}},{"name":"include_deprecated","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Deprecated"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SigmaRuleSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sigma/{rule_id}":{"get":{"tags":["Sigma Rules"],"summary":"Sigma Lookup","description":"Fetch a single Sigma rule by UUID.","operationId":"sigma_rule_lookup","security":[{"ContrastAPIKey":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","minLength":36,"maxLength":36,"pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Rule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SigmaRuleLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sigma/bulk":{"post":{"tags":["Sigma Rules"],"summary":"Sigma Bulk","description":"Bulk lookup up to 50 Sigma rules by UUID. Each rule_id consumes 1 unit of the\nper-hour quota; ids beyond the caller's remaining quota land in\n`skipped_due_to_rate_limit` instead of failing the whole batch — parity with\nbulk_cve_lookup / bulk_ioc_lookup (v1.27 dynamic budget).","operationId":"sigma_rule_bulk","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSigmaRuleLookupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSigmaRuleLookupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ContrastAPIKey":[]}]}}},"components":{"schemas":{"AbuseIpdbInfo":{"properties":{"status":{"type":"string","enum":["ok","skipped","rate_limited","error","pro_only"],"title":"Status","description":"'ok' = data fetched; 'skipped' = API key not configured; 'rate_limited' = AbuseIPDB quota exceeded; 'error' = transient HTTP/network failure; 'pro_only' = returned on Free tier as upsell hint (see upgrade_url)."},"abuse_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Abuse Score","description":"AbuseIPDB confidence-of-abuse score (0-100). Only present when status='ok'."},"total_reports":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Reports","description":"Number of reports submitted against this IP in the last 90 days."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code from AbuseIPDB geolocation (may differ from RIPE)."},"isp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Isp","description":"ISP name as reported by AbuseIPDB."},"usage_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usage Type","description":"AbuseIPDB usage classification: 'Data Center/Web Hosting/Transit', 'ISP', 'Mobile ISP', etc."},"is_tor":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Tor","description":"AbuseIPDB's Tor exit flag (cross-reference with top-level tor_exit field)."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Human-readable reason string. Present when status is skipped/rate_limited/error/pro_only."},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url","description":"Upgrade link returned when status='pro_only'."}},"type":"object","required":["status"],"title":"AbuseIpdbInfo","description":"AbuseIPDB reputation check (Pro tier only)."},"AsnResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"target":{"type":"string","title":"Target"},"resolved_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolved Ip"},"asn":{"type":"integer","title":"Asn"},"asn_name":{"type":"string","maxLength":256,"title":"Asn Name","default":""},"ipv4_prefixes":{"items":{"type":"string"},"type":"array","title":"Ipv4 Prefixes"},"ipv6_prefixes":{"items":{"type":"string"},"type":"array","title":"Ipv6 Prefixes"},"ipv4_count":{"type":"integer","title":"Ipv4 Count","default":0},"ipv6_count":{"type":"integer","title":"Ipv6 Count","default":0},"summary":{"type":"string","title":"Summary","default":""},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["target","asn"],"title":"AsnResponse"},"AtlasCaseStudyResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"case_study_id":{"type":"string","title":"Case Study Id","description":"Canonical ATLAS case study id, e.g. 'AML.CS0000'."},"name":{"type":"string","title":"Name","description":"Short title of the incident, e.g. 'Evasion of Deep Learning Detector'."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Narrative summary of the incident as published by MITRE ATLAS."},"techniques_used":{"items":{"type":"string"},"type":"array","title":"Techniques Used","description":"ATLAS technique ids used in this incident's procedure, in observed order."}},"additionalProperties":true,"type":"object","required":["case_study_id","name"],"title":"AtlasCaseStudyResponse","description":"MITRE ATLAS case study record — real-world AI/ML incidents."},"AtlasCaseStudySearchResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"query":{"additionalProperties":true,"type":"object","title":"Query","description":"Echo of input filters (keyword/technique_id)."},"total":{"type":"integer","title":"Total","description":"Number of case studies returned (capped at 200).","default":0},"results":{"items":{"$ref":"#/components/schemas/AtlasCaseStudyResponse"},"type":"array","title":"Results","description":"Matching case studies."}},"additionalProperties":true,"type":"object","title":"AtlasCaseStudySearchResponse","description":"List response for atlas_case_study_search."},"AtlasTechniqueListItem":{"properties":{"technique_id":{"type":"string","title":"Technique Id","description":"Canonical ATLAS technique id."},"name":{"type":"string","title":"Name","description":"Human-readable technique name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Full description; consider drilling into atlas_technique_lookup for context."},"tactics":{"items":{"type":"string"},"type":"array","title":"Tactics","description":"ATLAS tactic ids covering this technique. Sub-techniques inherit from parent; see inherited_tactics."},"inherited_tactics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Inherited Tactics","description":"True when tactics were inherited from the parent technique. Omitted when native."},"maturity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Maturity","description":"'demonstrated' or 'feasible'."},"attack_reference_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attack Reference Id","description":"Bridged ATT&CK id or null."},"subtechnique_of":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtechnique Of","description":"Parent technique id when applicable."}},"additionalProperties":true,"type":"object","required":["technique_id","name"],"title":"AtlasTechniqueListItem","description":"Slim ATLAS technique row for search results."},"AtlasTechniqueResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"technique_id":{"type":"string","title":"Technique Id","description":"Canonical ATLAS technique id, e.g. 'AML.T0000', 'AML.T0000.000'."},"name":{"type":"string","title":"Name","description":"Human-readable technique name, e.g. 'Search Open Technical Databases'."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Full technique description as published by MITRE ATLAS. May be multi-paragraph."},"tactics":{"items":{"type":"string"},"type":"array","title":"Tactics","description":"ATLAS tactic ids that this technique belongs to, e.g. ['AML.TA0002'] (Reconnaissance). Sub-techniques have empty tactics in upstream ATLAS; we backfill from the parent and set inherited_tactics=true when this happens."},"inherited_tactics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Inherited Tactics","description":"True when `tactics` was inherited from the parent technique (this is a sub-technique). Omitted when tactics are native to the record."},"maturity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Maturity","description":"MITRE ATLAS maturity classification: 'demonstrated' (observed in real attacks) or 'feasible' (theoretical)."},"attack_reference_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attack Reference Id","description":"Bridged ATT&CK technique id when ATLAS cites a parallel enterprise TTP, e.g. 'T1596'. About 20% of ATLAS techniques carry an ATT&CK reference; use this to pivot to D3FEND defenses."},"attack_reference_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attack Reference Url","description":"Canonical ATT&CK URL for the bridged technique, e.g. 'https://attack.mitre.org/techniques/T1596/'."},"subtechnique_of":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtechnique Of","description":"Parent technique id when this is a sub-technique, e.g. 'AML.T0000' for 'AML.T0000.000'."},"created_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created Date","description":"ISO-8601 date the technique was first published in ATLAS."},"modified_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified Date","description":"ISO-8601 date of the most recent ATLAS update for this technique."}},"additionalProperties":true,"type":"object","required":["technique_id","name"],"title":"AtlasTechniqueResponse","description":"MITRE ATLAS technique record (AI/ML attack catalog).\n\nATLAS catalogues adversarial techniques targeting AI/ML systems (LLM prompt\ninjection, model poisoning, evasion). About 80% of techniques have no ATT&CK\nbridge — ATLAS is the canonical reference for AI/ML-specific TTPs."},"AtlasTechniqueSearchResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"query":{"additionalProperties":true,"type":"object","title":"Query","description":"Echo of the input filters (keyword/tactic/maturity)."},"total":{"type":"integer","title":"Total","description":"Number of techniques returned (capped at 200).","default":0},"results":{"items":{"$ref":"#/components/schemas/AtlasTechniqueListItem"},"type":"array","title":"Results","description":"Matching ATLAS techniques."}},"additionalProperties":true,"type":"object","title":"AtlasTechniqueSearchResponse","description":"List response for atlas_technique_search."},"AuditResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (lowercased, no scheme)."},"report":{"anyOf":[{"$ref":"#/components/schemas/DomainReportResponse"},{"type":"null"}],"description":"Full domain intelligence report — same shape as /v1/domain/{domain}. Contains DNS, WHOIS, SSL, subdomains, threat intel, reputation, and verdict. See DomainReportResponse."},"technologies":{"$ref":"#/components/schemas/AuditTechInfo","description":"Technology fingerprint detected from live response headers. See AuditTechInfo."},"live_headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Live Headers","description":"Filtered HTTP response headers from the origin (lowercased keys). Sensitive headers (Set-Cookie, Authorization, etc.) are stripped before serialization."},"summary":{"type":"string","title":"Summary","description":"One-line audit summary combining domain report summary + technology count.","default":""}},"type":"object","required":["domain"],"title":"AuditResponse"},"AuditTechInfo":{"properties":{"technologies":{"items":{"$ref":"#/components/schemas/TechItem"},"type":"array","title":"Technologies","description":"Detected technologies (name + category) inferred from response headers (e.g. Server, X-Powered-By)."},"categories":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Categories","description":"Technologies grouped by category (e.g. {'cdn': ['Cloudflare'], 'webserver': ['nginx']})."},"count":{"type":"integer","title":"Count","description":"Total number of detected technologies (== sum of categories).","default":0},"summary":{"type":"string","title":"Summary","description":"One-line summary of the detected stack.","default":""}},"type":"object","title":"AuditTechInfo","description":"Technology fingerprint subset embedded in /v1/audit (no domain echo — outer AuditResponse carries it)."},"BrandAssetsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed)."},"fetched_url":{"type":"string","title":"Fetched Url","description":"Final URL we fetched, e.g. https://example.com/ (post-redirects)."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status returned by the homepage fetch."},"favicon_url_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Favicon Url Untrusted","description":"Resolved favicon URL (`<link rel='icon'>`, `shortcut icon`, `apple-touch-icon`, then `/favicon.ico` fallback). Absolute. `_untrusted`."},"og_image_url_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Og Image Url Untrusted","description":"`<meta property='og:image'>` resolved to an absolute URL. Used as the social-share thumbnail. `_untrusted`."},"theme_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Color","description":"`<meta name='theme-color'>` value (verbatim, capped at 64 chars). Useful for matching brand chrome."},"site_name_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Site Name Untrusted","description":"`<meta property='og:site_name'>` (preferred) or `<title>` fallback. Capped at 200 chars. `_untrusted`."},"logo_url_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url Untrusted","description":"`Organization.logo` from the first matching JSON-LD block (`<script type='application/ld+json'>`). Resolved to absolute URL. `_untrusted`."},"cache_respected":{"type":"boolean","title":"Cache Respected","description":"True if we wrote the result to our cache. False when the target sent `Cache-Control: no-store` or `private` and we honoured it (Guardrail #4 — we don't cache content the target asked us not to).","default":true},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["domain","fetched_url","status_code"],"title":"BrandAssetsResponse","description":"Public brand-identity assets scraped from a domain's homepage.\n\nWhat we DO: GET `https://{domain}/` (HTTP fallback), parse `<head>` for\nfavicon, `og:image`, `theme-color`, `og:site_name`, and JSON-LD\n`Organization.logo`. All URL fields are absolute and `_untrusted` (DO\nNOT execute, shell-out, or fetch from inside an LLM tool-use turn).\n\nEthical floor: we honour the target site's robots.txt — if it\nDisallows path \"/\" for our UA token (\"ContrastAPI\") OR for `*`, we\nreturn 403 `error.code = robots_txt_disallow` and DO NOT fetch."},"BulkAtlasTechniqueItem":{"properties":{"technique_id":{"type":"string","title":"Technique Id","description":"Echoed input ATLAS technique id (upper-cased + de-duplicated)."},"status":{"type":"string","enum":["ok","error","not_found","invalid_format"],"title":"Status","description":"Per-item outcome (v1.21.0+ unified across bulk_cve/bulk_ioc/bulk_atlas): 'ok' = technique populated; 'not_found' = id not in synced ATLAS catalog; 'invalid_format' = id failed AML.T#### / AML.T####.### regex; 'error' = transient lookup failure (DB I/O exception) — rare, server-side fallback only.","default":"ok"},"technique":{"anyOf":[{"$ref":"#/components/schemas/AtlasTechniqueResponse"},{"type":"null"}],"description":"Full ATLAS technique record when status='ok'. Same shape as /v1/atlas/{technique_id}."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Human-readable error message when status is 'not_found' or 'invalid_format'."}},"additionalProperties":true,"type":"object","required":["technique_id"],"title":"BulkAtlasTechniqueItem","description":"One ATLAS technique outcome inside a bulk_atlas_technique_lookup response."},"BulkAtlasTechniqueResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"results":{"items":{"$ref":"#/components/schemas/BulkAtlasTechniqueItem"},"type":"array","title":"Results","description":"Per-technique outcome list, preserving input order after upper-case de-duplication."},"total":{"type":"integer","title":"Total","description":"Total number of unique technique IDs submitted (== processed + len(skipped_due_to_rate_limit)).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of items actually looked up (== len(results)). Equal to total unless dynamic-budget partial-fill kicked in.","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"Technique IDs that were not processed because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available."},"successful":{"type":"integer","title":"Successful","description":"Count of items with status='ok'.","default":0},"failed":{"type":"integer","title":"Failed","description":"Count of items with status='not_found' or 'invalid_format'.","default":0},"partial":{"type":"boolean","title":"Partial","description":"True when at least one item was not_found, invalid_format, or skipped due to rate limit.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line aggregate summary (e.g. '4/5 techniques found').","default":""}},"additionalProperties":true,"type":"object","title":"BulkAtlasTechniqueResponse"},"BulkCveItem":{"properties":{"cve_id":{"type":"string","title":"Cve Id","description":"Echoed input CVE identifier (upper-cased + de-duplicated)."},"status":{"type":"string","enum":["ok","error","not_found","invalid_format"],"title":"Status","description":"Per-item outcome. 'ok' = cve populated; 'not_found' = CVE not in local cve.db (likely reserved or post-cutoff); 'invalid_format' = ID failed CVE-YYYY-NNNN+ regex; 'error' = lookup failed (transient).","default":"ok"},"cve":{"anyOf":[{"$ref":"#/components/schemas/CveResponse"},{"type":"null"}],"description":"Full CVE record when status='ok'. Same shape as /v1/cve/{cve_id}."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Human-readable error message when status is 'error' or 'not_found'."}},"type":"object","required":["cve_id"],"title":"BulkCveItem"},"BulkCveResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"results":{"items":{"$ref":"#/components/schemas/BulkCveItem"},"type":"array","title":"Results","description":"Per-CVE outcome list, preserving input order after upper-case de-duplication."},"total":{"type":"integer","title":"Total","description":"Total number of unique CVE IDs submitted (== processed + len(skipped_due_to_rate_limit)).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of items actually looked up (== len(results)). Equal to total unless dynamic-budget partial-fill kicked in.","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"CVE IDs that were not processed because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available."},"successful":{"type":"integer","title":"Successful","description":"Count of items with status='ok'.","default":0},"failed":{"type":"integer","title":"Failed","description":"Count of items with status='error' (transient lookup failure).","default":0},"timed_out":{"type":"integer","title":"Timed Out","description":"Count of items that hit the per-CVE or overall timeout.","default":0},"partial":{"type":"boolean","title":"Partial","description":"True when at least one item failed, timed out, was not_found, or skipped due to rate limit.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line aggregate summary (e.g. '45/50 CVEs found').","default":""}},"type":"object","title":"BulkCveResponse"},"BulkDomainItem":{"properties":{"domain":{"type":"string","title":"Domain","description":"Echoed input domain (lowercased)."},"status":{"type":"string","enum":["ok","error"],"title":"Status","description":"Per-item outcome. 'ok' = report populated; 'error' = error populated (timeout / lookup failed / invalid).","default":"ok"},"report":{"anyOf":[{"$ref":"#/components/schemas/DomainReportResponse"},{"type":"null"}],"description":"Full domain intelligence report when status='ok'. Same shape as /v1/domain/{domain}."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Human-readable error message when status='error' (timeout, validation failure, upstream error)."}},"type":"object","required":["domain"],"title":"BulkDomainItem"},"BulkDomainResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"results":{"items":{"$ref":"#/components/schemas/BulkDomainItem"},"type":"array","title":"Results","description":"Per-domain outcome list, preserving the input order."},"total":{"type":"integer","title":"Total","description":"Total number of input domains submitted (== processed + len(skipped_due_to_rate_limit)).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of domains actually scanned (== len(results)). Equal to total unless dynamic-budget partial-fill kicked in.","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"Domains that were not processed because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available. Distinct from per-domain timeouts (those still consume budget and surface as status='error' in results)."},"successful":{"type":"integer","title":"Successful","description":"Count of items with status='ok'.","default":0},"failed":{"type":"integer","title":"Failed","description":"Count of items with status='error' from non-timeout failures.","default":0},"timed_out":{"type":"integer","title":"Timed Out","description":"Count of items that hit the per-domain or overall timeout.","default":0},"partial":{"type":"boolean","title":"Partial","description":"True when at least one item failed, timed out, or was skipped due to rate limit.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line aggregate summary (e.g. '8/10 domains succeeded').","default":""}},"type":"object","title":"BulkDomainResponse"},"BulkIocItem":{"properties":{"indicator":{"type":"string","title":"Indicator","description":"Echoed input indicator (sanitized; type auto-detected per-item)."},"status":{"type":"string","enum":["ok","error","not_found","invalid_format"],"title":"Status","description":"Per-item outcome (v1.21.0+ unified across bulk_cve/bulk_ioc/bulk_atlas): 'ok' = ioc populated; 'invalid_format' = indicator failed validation (empty / unknown type / private IP); 'error' = transient lookup failure (timeout / upstream error); 'not_found' is reserved for parity with bulk_cve_lookup — IOC queries always reach upstream feeds, so this value is rarely emitted (treat as semantic equivalent of 'ok' with threat_level='none' and empty sources).","default":"ok"},"ioc":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Ioc","description":"Slim IOC enrichment when status='ok' — keys: type, threat_level, sources. Bulk endpoint omits indicator/summary/verdict (use /v1/ioc/{indicator} for the full IocResponse shape). Per-source dicts may carry richer fields than the single endpoint (raw urlhaus dict instead of {found, urls_online})."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Human-readable error message when status='error' (timeout, invalid indicator, upstream error)."}},"additionalProperties":true,"type":"object","required":["indicator"],"title":"BulkIocItem"},"BulkIocResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"results":{"items":{"$ref":"#/components/schemas/BulkIocItem"},"type":"array","title":"Results","description":"Per-indicator outcome list, preserving input order."},"total":{"type":"integer","title":"Total","description":"Total number of input indicators submitted (== processed + len(skipped_due_to_rate_limit)).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of items actually enriched (== len(results)). Equal to total unless dynamic-budget partial-fill kicked in.","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"Indicators that were not processed because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available."},"successful":{"type":"integer","title":"Successful","description":"Count of items with status='ok'.","default":0},"failed":{"type":"integer","title":"Failed","description":"Count of items with status='error' from non-timeout failures.","default":0},"timed_out":{"type":"integer","title":"Timed Out","description":"Count of items that hit the per-IOC or overall timeout.","default":0},"invalid":{"type":"integer","title":"Invalid","description":"Count of items with status='invalid_format' (validation rejection: empty / unknown type / private IP). Distinct from `failed` which counts only transient errors. `successful + failed + timed_out + invalid == processed` always holds.","default":0},"partial":{"type":"boolean","title":"Partial","description":"True when at least one item failed, timed out, was invalid, or skipped due to rate limit.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line aggregate summary (e.g. '12/15 indicators enriched').","default":""}},"type":"object","title":"BulkIocResponse"},"BulkSigmaRuleLookupItem":{"properties":{"rule_id":{"type":"string","title":"Rule Id","description":"Echoed input rule UUID"},"status":{"type":"string","enum":["ok","not_found","invalid_format"],"title":"Status","description":"'ok' = rule found; 'not_found' = UUID not in index; 'invalid_format' = invalid UUID"},"rule":{"anyOf":[{"$ref":"#/components/schemas/SigmaRule"},{"type":"null"}],"description":"Full rule when status='ok'"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message when status != 'ok'"}},"additionalProperties":false,"type":"object","required":["rule_id","status"],"title":"BulkSigmaRuleLookupItem","description":"Single item in bulk lookup response."},"BulkSigmaRuleLookupRequest":{"properties":{"rule_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Rule Ids","description":"UUIDs to look up (1-50)"}},"additionalProperties":false,"type":"object","required":["rule_ids"],"title":"BulkSigmaRuleLookupRequest","description":"POST /v1/sigma/bulk request body."},"BulkSigmaRuleLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up tool calls (atlas_technique_lookup, cve_lookup, etc.)"},"results":{"items":{"$ref":"#/components/schemas/BulkSigmaRuleLookupItem"},"type":"array","title":"Results","description":"Per-rule outcome, preserving input order"},"total":{"type":"integer","title":"Total","description":"Total unique rule IDs submitted (== processed + len(skipped_due_to_rate_limit)).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of items actually looked up (== len(results)). Equal to total unless dynamic-budget partial-fill kicked in.","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"Rule UUIDs not processed because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available."},"successful":{"type":"integer","title":"Successful","description":"Count of items with status='ok'","default":0},"failed":{"type":"integer","title":"Failed","description":"Count of items with status != 'ok'","default":0},"partial":{"type":"boolean","title":"Partial","description":"True when at least one item was not_found, invalid_format, or skipped due to rate limit.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line aggregate (e.g., '3/5 rules found')","default":""}},"additionalProperties":false,"type":"object","title":"BulkSigmaRuleLookupResponse","description":"Bulk rule lookup response."},"CertificateSummary":{"properties":{"issuer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issuer","description":"Cert issuer CN or O."},"not_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Not Before","description":"notBefore timestamp."},"not_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Not After","description":"notAfter timestamp."},"common_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Common Name","description":"Cert Subject CN."}},"additionalProperties":true,"type":"object","title":"CertificateSummary","description":"Single cert entry inside CertificatesInfo.certificates."},"CertificatesInfo":{"properties":{"total_certificates":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Certificates","description":"Total cert count from crt.sh (pre-dedup)."},"certificates":{"anyOf":[{"items":{"$ref":"#/components/schemas/CertificateSummary"},"type":"array"},{"type":"null"}],"title":"Certificates","description":"Up to CT_MAX_CERTS recent unique certs (deduped by serial)."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Populated when the crt.sh fetch failed (e.g. 'crt_sh_timeout', 'crt_sh_rate_limited', 'crt_sh_unavailable'). Distinguishes 'no certs found' from 'fetch failed'; risk_score skips the CT factor when this is set."},"crtsh_status":{"anyOf":[{"type":"string","enum":["ok","timeout","rate_limited","unavailable","error"]},{"type":"null"}],"title":"Crtsh Status","description":"Status of the crt.sh fetch behind certificates. Mirrors subdomains.crtsh_status so both halves of a domain_report agree on whether CT logs delivered. 'ok' means the upstream responded — total_certificates=0 with status='ok' is a real empty result. Anything else (timeout / rate_limited / unavailable / error) means the upstream did not deliver and the cert list may be missing entries."}},"additionalProperties":true,"type":"object","title":"CertificatesInfo"},"CertsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"total_certificates":{"type":"integer","title":"Total Certificates","default":0},"certificates":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Certificates"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain"],"title":"CertsResponse"},"CheckHeadersResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"findings":{"items":{"$ref":"#/components/schemas/HeaderFinding"},"type":"array","title":"Findings","description":"Per-header validation findings — one entry per header you submitted that the validator recognized."},"total":{"type":"integer","title":"Total","description":"Total number of findings emitted (== len(findings)).","default":0},"by_severity":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Severity","description":"Finding counts bucketed by severity, e.g. {'high': 2, 'medium': 1, 'low': 0}."},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary of grade + key issues.","default":""},"score":{"type":"integer","title":"Score","description":"Aggregate header-posture score (0-100) computed from per-finding severity weights.","default":0},"grade":{"type":"string","enum":["A","B","C","D","F"],"title":"Grade","description":"Letter grade derived from score: A=90+, B=75+, C=60+, D=40+, else F.","default":"F"},"headers_present":{"items":{"type":"string"},"type":"array","title":"Headers Present","description":"Header names from the submitted set that the validator recognized as present."},"headers_missing":{"items":{"type":"string"},"type":"array","title":"Headers Missing","description":"Header names the ruleset expects but were not present in the submitted set."}},"type":"object","title":"CheckHeadersResponse"},"CipherInfo":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Cipher suite name as reported by OpenSSL, e.g. 'TLS_AES_256_GCM_SHA384' (TLS 1.3) or 'ECDHE-RSA-AES256-GCM-SHA384' (TLS 1.2). Null on handshake failure."},"protocol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Protocol","description":"TLS protocol version negotiated for this cipher, e.g. 'TLSv1.3', 'TLSv1.2'. Mirrors SslResponse.protocol and is null on handshake failure."},"bits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bits","description":"Effective symmetric key length in bits (e.g. 256 for AES-256-GCM). Null on handshake failure."}},"additionalProperties":true,"type":"object","title":"CipherInfo"},"CodeCheckResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"findings":{"items":{"$ref":"#/components/schemas/CodeFinding"},"type":"array","title":"Findings","description":"Per-rule findings emitted by the scanner. Empty when the code is clean."},"total":{"type":"integer","title":"Total","description":"Total number of findings (== len(findings)).","default":0},"by_severity":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Severity","description":"Finding counts bucketed by severity, e.g. {'critical': 1, 'high': 2, 'medium': 0, 'low': 1}."},"summary":{"type":"string","title":"Summary","description":"One-line summary aggregating finding counts by severity.","default":""}},"type":"object","title":"CodeCheckResponse"},"CodeFinding":{"properties":{"type":{"type":"string","title":"Type","description":"Rule identifier that fired (e.g. 'aws_secret_key', 'sql_injection'). Stable across releases.","default":""},"severity":{"type":"string","enum":["critical","high","medium","low"],"title":"Severity","description":"Impact bucket assigned by the rule. 'critical'/'high' are typically actionable; 'low' is advisory.","default":"medium"},"line":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Line","description":"1-indexed line number in the submitted code where the rule matched. Null if line cannot be determined."},"match":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Match","description":"Snippet of the matching text (truncated for ReDoS safety). Null when the rule does not capture text."},"description":{"type":"string","title":"Description","description":"Human-readable explanation of what the rule detects.","default":""},"remediation":{"type":"string","title":"Remediation","description":"Actionable fix or mitigation guidance.","default":""}},"type":"object","title":"CodeFinding"},"CodeInput":{"properties":{"code":{"type":"string","title":"Code","description":"Source code snippet to scan. Plain text; no length cap, but each scanner has its own per-line caps (ReDoS protection). Submit only code you have authorization to share — content is processed in-memory and not persisted."},"language":{"type":"string","title":"Language","description":"Source language hint for comment-stripping and rule selection. Allowed: generic, python, javascript, typescript, java, go, ruby, shell, bash. Use 'generic' if unknown — falls back to language-agnostic patterns.","default":"generic"}},"type":"object","required":["code"],"title":"CodeInput"},"CveResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"cve_id":{"type":"string","title":"Cve Id","description":"Canonical CVE identifier, e.g. 'CVE-2021-44228'."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"Human-readable one-line summary built from severity, CVSS, KEV status, and EPSS."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Full vulnerability description sourced from NVD/MITRE/GHSA."},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity","description":"CVSS v3 severity label: 'critical', 'high', 'medium', 'low', or 'none'."},"cvss_v3":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cvss V3","description":"CVSS v3.x base score (0.0-10.0). Null if no CVSS data available."},"cvss_breakdown":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Cvss Breakdown","description":"Per-metric CVSS v3 breakdown (attack_vector, attack_complexity, privileges_required, user_interaction, scope, confidentiality, integrity, availability). Keys present only when parsed from vector string."},"cwe_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cwe Id","description":"Primary CWE identifier, e.g. 'CWE-502'. First CWE when multiple are assigned."},"cwes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cwes","description":"All CWE identifiers assigned to this CVE, Primary first then Secondary. Null on legacy cache rows; cwe_id is always populated when cwes is non-empty."},"vulnerability_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vulnerability Status","description":"NVD lifecycle status: 'Analyzed', 'Modified', 'Awaiting Analysis', 'Deferred', 'Rejected', 'Withdrawn'. Drives verdict.completeness downgrade for Rejected/Withdrawn/Awaiting Analysis."},"cve_tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cve Tags","description":"NVD cveTags (e.g. 'disputed', 'unsupported-when-assigned', 'exclusively-hosted-service'). When 'disputed' is present, summary is prefixed with [DISPUTED]."},"epss":{"$ref":"#/components/schemas/EpssInfo","description":"Exploit Prediction Scoring System: score (0.0-1.0 probability) and percentile (0.0-100.0)."},"kev":{"$ref":"#/components/schemas/KevInfo","description":"CISA Known Exploited Vulnerabilities catalog: in_kev flag, date_added, plus 10 fields populated when in_kev=true (due_date, required_action, known_ransomware_use, vendor_project, product, vulnerability_name, short_description, notes, cwes, date_removed)."},"affected_products":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Affected Products","description":"CPE affected products. Truncated to first 20 by default. For GET /v1/cve/{cve_id}, use ?include_affected_products=true; for POST /v1/cves/bulk, set body field \"include_affected_products\": true."},"total_products":{"type":"integer","title":"Total Products","description":"Honest count of all affected products in the CVE database. Always present (emitted even when 0); matches len(affected_products) when not truncated.","default":0},"published":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published","description":"ISO 8601 publication timestamp from NVD/MITRE."},"modified":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified","description":"ISO 8601 last-modified timestamp; advances on NVD/MITRE revisions."},"references":{"items":{"type":"string"},"type":"array","title":"References","description":"Advisory URLs (vendor bulletins, patch commits, exploit PoCs, analysis writeups). Truncated to first 10 by default. For GET /v1/cve/{cve_id}, use ?include_full_references=true; for POST /v1/cves/bulk, set body field \"include_full_references\": true. Patch URL detection always runs against the full list — patch_url/patch_available are unaffected by the cap."},"total_references":{"type":"integer","title":"Total References","description":"Honest count of all references in the CVE database. Always present (emitted even when 0); matches len(references) when not truncated.","default":0},"total_references_unique":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total References Unique","description":"Unique-URL upstream count from refs_with_tags (Batch 6A). None for legacy cached rows synced before v1.29.x; populated after the next sync run for all 4 sources (NVD/MITRE/GHSA/OSV)."},"references_full":{"anyOf":[{"items":{"$ref":"#/components/schemas/ReferenceItem"},"type":"array"},{"type":"null"}],"title":"References Full","description":"Structured references with NVD reference tags + source provenance. Populated only when ?include_reference_tags=true (or body field include_reference_tags=true for bulk_cve_lookup). Default None for backward compat. Same truncation as `references` field — use include_full_references=true for the complete list."},"sources":{"items":{"type":"string"},"type":"array","title":"Sources","description":"Data sources that wrote the CVE record itself: 'nvd', 'mitre', 'ghsa', 'osv'. EPSS and KEV are tracked separately — see the top-level epss.* and kev.* fields, not this list."},"first_seen_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen Source","description":"First source that introduced this CVE into the local DB (for provenance/auditing)."},"first_seen_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen At","description":"ISO 8601 timestamp when this CVE was first ingested locally."},"patch_available":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Patch Available","description":"True when a vendor patch URL was detected in references (allowlisted vendor patterns). Null when enrichment was not requested."},"patch_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Patch Url","description":"First matched vendor patch/advisory URL from an allowlist (GitHub, RedHat, Ubuntu, Debian, Microsoft MSRC, Apple, Fortinet, Linux kernel.org, Cisco). Open-redirect params are filtered. Verify the host before clicking. Null when no match."},"related_cves":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Related Cves","description":"Up to 5 CVEs sharing affected products, ordered by severity DESC. Each item: {cve_id, severity, cvss_v3}. Null when enrichment was not requested."},"cvss_v2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cvss V2","description":"CVSS v2.0 base score (0.0-10.0). Always present (additive, non-opt-in); null for CVEs with no v2 metric or for legacy cached rows synced before v1.29.x. Pre-2010 CVEs and select dual-scored entries have v2-only or v2+v3 metrics."},"cvss_v2_vector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cvss V2 Vector","description":"CVSS v2.0 vector string, e.g. 'AV:N/AC:L/Au:N/C:C/I:C/A:C'. Null when cvss_v2 is None."},"severity_sources":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Severity Sources","description":"Multi-source severity breakdown (one entry per source: nvd, mitre, ghsa, osv). Populated only when ?include_severity_breakdown=true (or body field include_severity_breakdown=true for bulk_cve_lookup). Each entry has {source, severity, cvss_v3, cvss_v2}. Use to inspect vendor disputes (e.g. CVE-2023-38545 NVD-CRITICAL vs GHSA-HIGH)."},"severity_consensus":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity Consensus","description":"Majority-bucket consensus severity computed from severity_sources. On a tie, the highest severity wins (CRITICAL > HIGH > MEDIUM > LOW > NONE). Null when severity_breakdown was not requested or when no source reported a severity."},"severity_disagreement":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Severity Disagreement","description":"True when 2+ severity buckets appear across severity_sources entries — counts both across-source disagreement (NVD CRITICAL vs GHSA HIGH) and within-source v2-vs-v3 disagreement (v2 HIGH vs v3 MEDIUM on the same NVD entry). Null when severity_breakdown was not requested."}},"type":"object","required":["cve_id"],"title":"CveResponse"},"CveSearchItem":{"properties":{"cve_id":{"type":"string","title":"Cve Id","description":"Canonical CVE identifier, e.g. 'CVE-2021-44228'."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"Human-readable one-line summary."},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity","description":"CVSS v3 severity label."},"cvss_v3":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cvss V3","description":"CVSS v3.x base score (0.0-10.0)."},"cwe_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cwe Id","description":"Primary CWE identifier (legacy single-value field). Equals cwes[0] when cwes is present. Kept for backward-compat; consumers should prefer cwes."},"cwes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cwes","description":"Multi-CWE list as published by NVD. Mirrors cve_lookup.cwes (v1.28.0 multi-CWE adoption). Null/omitted when DB row has no multi-CWE list — legacy cwe_id still emitted."},"epss":{"$ref":"#/components/schemas/EpssInfo","description":"EPSS score + percentile."},"kev":{"$ref":"#/components/schemas/KevInfo","description":"CISA KEV status."},"total_products":{"type":"integer","title":"Total Products","description":"Honest count of affected products in DB.","default":0},"references_count":{"type":"integer","title":"References Count","description":"Honest count of upstream references for this CVE. Use this to decide whether a cve_lookup pivot is worthwhile (e.g. >0 refs → drill down; 0 → skip). The reference list itself is NOT included in slim search items — pass include=full or call cve_lookup to retrieve URLs.","default":0},"published":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published","description":"ISO 8601 publication timestamp."},"modified":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified","description":"ISO 8601 last-modified timestamp."},"sources":{"items":{"type":"string"},"type":"array","title":"Sources","description":"Source feeds for this CVE row."}},"additionalProperties":true,"type":"object","required":["cve_id"],"title":"CveSearchItem","description":"Slim per-result shape for cve_search list items.\n\nDefault cve_search response uses this shape (description / cvss_breakdown /\naffected_products / references / first_seen_* are dropped). Pass cve_search\n?include=full to get the full CveResponse shape — extra=\"allow\" lets the\nfull-mode fields pass through without a schema fork."},"CveSearchResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"count":{"type":"integer","title":"Count","description":"Number of CVEs in this page (== len(results)). Capped by `limit`.","default":0},"total":{"type":"integer","title":"Total","description":"Total CVE matches in the database for the query — the honest pre-pagination count.","default":0},"truncated":{"type":"boolean","title":"Truncated","description":"True when total > offset + count (more pages available — use next_offset).","default":false},"offset":{"type":"integer","title":"Offset","description":"Offset of the first item in this page (echoed from input).","default":0},"summary":{"type":"string","title":"Summary","description":"One-line summary like '50 CVEs returned, 1234 total (product=nginx, severity=HIGH)'.","default":""},"results":{"items":{"$ref":"#/components/schemas/CveSearchItem"},"type":"array","title":"Results","description":"Per-CVE slim records — see CveSearchItem."},"query_echo":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Query Echo","description":"Echoed search filters with empty values stripped. Keys: product, vendor, severity, cwe_id, published_after, published_before, kev, epss_min, cvss_min, cvss_max, sort, limit, offset, tagged. Useful for verifying the parsed query matched the intent."},"next_offset":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Offset","description":"Offset to pass on the next page. Null when truncated=False (no more results)."},"hint":{"anyOf":[{"$ref":"#/components/schemas/SearchHint"},{"type":"null"}],"description":"Pivot/refine hint emitted when the query returned 0 results or is overly broad."}},"type":"object","title":"CveSearchResponse"},"CveVulnItem":{"properties":{"cve_id":{"type":"string","title":"Cve Id"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"cvss_v3":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cvss V3"},"epss_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Epss Score"},"in_kev":{"type":"boolean","title":"In Kev","default":false}},"type":"object","required":["cve_id"],"title":"CveVulnItem"},"CvssDetailsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"version":{"type":"string","title":"Version","description":"CVSS specification version, e.g. '3.1' or '3.0'."},"vector":{"type":"string","title":"Vector","description":"Canonicalized CVSS vector string."},"base_score":{"type":"number","title":"Base Score","description":"Recomputed base score (0.0-10.0)."},"base_severity":{"type":"string","title":"Base Severity","description":"Base severity label: NONE / LOW / MEDIUM / HIGH / CRITICAL."},"metrics":{"$ref":"#/components/schemas/CvssMetrics","description":"Per-metric human-readable breakdown."},"temporal_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temporal Score","description":"Temporal score when temporal metrics are explicit; null otherwise."},"environmental_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Environmental Score","description":"Environmental score when env metrics are explicit; null otherwise."},"summary":{"type":"string","title":"Summary","description":"One-line agent-friendly summary."}},"type":"object","required":["version","vector","base_score","base_severity","metrics","summary"],"title":"CvssDetailsResponse","description":"Per-metric breakdown of a CVSS v3.x vector string."},"CvssMetrics":{"properties":{"attack_vector":{"type":"string","title":"Attack Vector","description":"AV — NETWORK / ADJACENT_NETWORK / LOCAL / PHYSICAL."},"attack_complexity":{"type":"string","title":"Attack Complexity","description":"AC — LOW / HIGH."},"privileges_required":{"type":"string","title":"Privileges Required","description":"PR — NONE / LOW / HIGH."},"user_interaction":{"type":"string","title":"User Interaction","description":"UI — NONE / REQUIRED."},"scope":{"type":"string","title":"Scope","description":"S — UNCHANGED / CHANGED."},"confidentiality_impact":{"type":"string","title":"Confidentiality Impact","description":"C — NONE / LOW / HIGH."},"integrity_impact":{"type":"string","title":"Integrity Impact","description":"I — NONE / LOW / HIGH."},"availability_impact":{"type":"string","title":"Availability Impact","description":"A — NONE / LOW / HIGH."}},"type":"object","required":["attack_vector","attack_complexity","privileges_required","user_interaction","scope","confidentiality_impact","integrity_impact","availability_impact"],"title":"CvssMetrics"},"CweLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"cwe_id":{"type":"string","title":"Cwe Id","description":"Canonical CWE identifier, e.g. 'CWE-79', 'CWE-502'."},"name":{"type":"string","title":"Name","description":"Short human-readable weakness name, e.g. 'Improper Neutralization of Input During Web Page Generation'."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"MITRE one-paragraph summary of the weakness."},"extended_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Extended Description","description":"MITRE's longer-form explanation including consequences and typical exploitation paths."},"abstract_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Abstract Type","description":"MITRE 'Weakness Abstraction' level: 'Pillar' (most abstract), 'Class', 'Base', 'Variant' (most specific), or 'Compound'."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Catalog lifecycle status: 'Stable', 'Draft', 'Incomplete', 'Deprecated', or 'Obsolete'. Prefer Stable when chaining to other tools."},"likelihood":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Likelihood","description":"MITRE's 'Likelihood of Exploit' rating: 'High', 'Medium', 'Low', or null when unrated."},"mitigations":{"items":{"type":"string"},"type":"array","maxItems":30,"title":"Mitigations","description":"Recommended mitigations as 'Phase — Description' strings, parsed from MITRE's 'Potential Mitigations' field (Architecture and Design, Implementation, etc.)."},"examples":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Examples","description":"Observed example CVEs as 'CVE-x: description' strings. These are MITRE-curated exemplars, not an exhaustive list — use cve_search?cwe= for the full list."},"parent_cwe":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Cwe","description":"Direct parent CWE in research view 1000 (Primary ChildOf), e.g. 'CWE-707'. Call cwe_lookup with this value to traverse up the weakness hierarchy."},"child_cwes":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Child Cwes","description":"Direct child CWEs in research view 1000 (ParentOf entries). Call cwe_lookup on any entry to traverse down to a more specific weakness."},"cve_count":{"type":"integer","title":"Cve Count","description":"Number of CVEs in our database whose primary cwe_id equals this CWE. Lower bound — upstream CVEs may map to multiple CWEs but our schema stores only the primary. Use cve_search?cwe=<id> for the actual list.","default":0},"total_mitigations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Mitigations","description":"Honest pre-truncation count of mitigation entries from MITRE. When the slim default is used, mitigations is capped to the first 3 — compare to total_mitigations to decide whether to refetch with include=full."},"total_examples":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Examples","description":"Honest pre-truncation count of example CVEs from MITRE. When the slim default is used, examples is capped to the first 3 — compare to total_examples to decide whether to refetch with include=full."},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO 8601 timestamp of the last sync from MITRE's CSV catalog."}},"additionalProperties":true,"type":"object","required":["cwe_id","name"],"title":"CweLookupResponse","description":"MITRE CWE catalog record (research view 1000).\n\nText fields are sourced verbatim from MITRE's published CSV and JSON-encoded —\nsafe for JSON consumers, but downstream callers that render into HTML must apply\ntheir own escaping. `extra=\"allow\"` is set for forward-compat (Tier 2 audit pattern,\nSession 171)."},"D3fendCoverageBody":{"properties":{"attack_technique_ids":{"items":{"type":"string"},"type":"array","maxItems":500,"title":"Attack Technique Ids","description":"List of ATT&CK T-codes (T####, T####.###). Truncated to 500 entries before query."}},"type":"object","title":"D3fendCoverageBody","description":"POST body for /v1/d3fend/coverage — accepts a batch of ATT&CK T-codes."},"D3fendCoverageResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"queried_techniques":{"items":{"type":"string"},"type":"array","title":"Queried Techniques","description":"The ATT&CK T-codes the caller queried (truncated to 500 if larger)."},"coverage_by_tactic":{"additionalProperties":{"type":"integer"},"type":"object","title":"Coverage By Tactic","description":"Distinct D3FEND defenses per tactic across all queried techniques. Keys are tactics (Harden/Detect/Isolate/...), values are counts."},"defended_techniques":{"items":{"type":"string"},"type":"array","title":"Defended Techniques","description":"Subset of queried techniques that have at least one D3FEND defense."},"undefended_techniques":{"items":{"type":"string"},"type":"array","title":"Undefended Techniques","description":"Subset of queried techniques with NO D3FEND mapping — gap candidates."}},"additionalProperties":true,"type":"object","title":"D3fendCoverageResponse","description":"Batch coverage breakdown for a list of ATT&CK T-codes."},"D3fendDefenseForAttackItem":{"properties":{"defense_id":{"type":"string","title":"Defense Id","description":"D3FEND defense slug."},"label":{"type":"string","title":"Label","description":"Human-readable defense name."},"uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uri","description":"Full D3FEND ontology URI. Omitted in slim default; pass include=full to get it back."},"parent_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Label","description":"Parent defense category."},"tactic":{"type":"string","title":"Tactic","description":"D3FEND tactic — one of Model/Harden/Detect/Isolate/Deceive/Evict/Restore."},"artifact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifact","description":"Targeted digital artifact."},"attack_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attack Label","description":"Original ATT&CK technique label as published by MITRE."},"attack_tactic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attack Tactic","description":"ATT&CK tactic the technique sits under."}},"additionalProperties":true,"type":"object","required":["defense_id","label","tactic"],"title":"D3fendDefenseForAttackItem","description":"One defense entry in a reverse-lookup result."},"D3fendDefenseListItem":{"properties":{"defense_id":{"type":"string","title":"Defense Id","description":"D3FEND defense slug."},"label":{"type":"string","title":"Label","description":"Human-readable defense name."},"uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Uri","description":"Full D3FEND ontology URI. Omitted in slim default; pass include=full to get it back."},"parent_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Label","description":"Parent defense category."},"tactic":{"type":"string","title":"Tactic","description":"D3FEND tactic."},"artifact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifact","description":"Targeted digital artifact."}},"additionalProperties":true,"type":"object","required":["defense_id","label","tactic"],"title":"D3fendDefenseListItem","description":"Slim D3FEND defense row for search results (no attack_techniques list)."},"D3fendDefenseResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"defense_id":{"type":"string","title":"Defense Id","description":"Slug derived from the D3FEND ontology URI fragment, e.g. 'TokenBinding', 'FileHashing'."},"label":{"type":"string","title":"Label","description":"Human-readable defense name, e.g. 'Token Binding', 'File Hashing'."},"uri":{"type":"string","title":"Uri","description":"Full D3FEND ontology URI, e.g. 'http://d3fend.mitre.org/ontologies/d3fend.owl#TokenBinding'."},"parent_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Label","description":"Parent defense category, e.g. 'Credential Hardening' for 'Token Binding'."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"D3FEND-published description of the defense (may be null in current sync)."},"tactic":{"type":"string","title":"Tactic","description":"One of seven D3FEND tactics: Model, Harden, Detect, Isolate, Deceive, Evict, Restore."},"artifact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifact","description":"Digital artifact the defense targets, e.g. 'Access Token', 'File', 'Process'."},"attack_techniques":{"items":{"type":"string"},"type":"array","title":"Attack Techniques","description":"ATT&CK T-codes this defense mitigates, e.g. ['T1550.001', 'T1539']. Drill via cve_search or d3fend_defense_for_attack to bridge."}},"additionalProperties":true,"type":"object","required":["defense_id","label","uri","tactic"],"title":"D3fendDefenseResponse","description":"MITRE D3FEND defense technique record.\n\nD3FEND catalogues defensive techniques against ATT&CK TTPs. Each defense is\nclassified into one of 7 tactics (Model, Harden, Detect, Isolate, Deceive,\nEvict, Restore) and may target a specific digital artifact (e.g. 'Access\nToken', 'Process'). Use attack_techniques to see which ATT&CK T-codes this\ndefense mitigates."},"D3fendDefenseSearchResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"query":{"additionalProperties":true,"type":"object","title":"Query","description":"Echo of input filters (keyword/tactic/artifact)."},"total":{"type":"integer","title":"Total","description":"Number of defenses returned (capped at 200).","default":0},"results":{"items":{"$ref":"#/components/schemas/D3fendDefenseListItem"},"type":"array","title":"Results","description":"Matching D3FEND defenses."}},"additionalProperties":true,"type":"object","title":"D3fendDefenseSearchResponse","description":"List response for d3fend_defense_search."},"D3fendForAttackResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"attack_technique_id":{"type":"string","title":"Attack Technique Id","description":"The ATT&CK T-code that was queried, e.g. 'T1059'."},"total":{"type":"integer","title":"Total","description":"Honest pre-truncation count of D3FEND defenses that mitigate this technique.","default":0},"truncated":{"type":"boolean","title":"Truncated","description":"True when defenses[] was capped at `limit`. Inspect `total` for the full count and re-call with a higher `limit` if needed.","default":false},"defenses":{"items":{"$ref":"#/components/schemas/D3fendDefenseForAttackItem"},"type":"array","title":"Defenses","description":"D3FEND defenses mapped to this ATT&CK technique (capped at request `limit`, default 30)."},"coverage_by_tactic":{"additionalProperties":{"type":"integer"},"type":"object","title":"Coverage By Tactic","description":"Defense count per D3FEND tactic for this single technique, e.g. {'Harden': 3, 'Detect': 5}."}},"additionalProperties":true,"type":"object","required":["attack_technique_id"],"title":"D3fendForAttackResponse","description":"Reverse lookup response: given an ATT&CK T-code, list mitigating D3FEND defenses."},"DepFinding":{"properties":{"package":{"type":"string","title":"Package"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"cve_id":{"type":"string","title":"Cve Id"},"severity":{"type":"string","title":"Severity","default":"unknown"},"cvss_v3":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cvss V3"},"description":{"type":"string","title":"Description","default":""},"epss_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Epss Score"},"in_kev":{"type":"boolean","title":"In Kev","default":false},"fixed_in":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fixed In","description":"First patched release per NVD/MITRE version range data (CVE affected_products[].version_end). Excluded from the wire (response_model_exclude_none=True) when the matched range is open-ended or no input version was supplied — in those cases inspect remediation copy."},"remediation":{"type":"string","title":"Remediation","default":""}},"type":"object","required":["package","cve_id"],"title":"DepFinding"},"DependenciesInput":{"properties":{"packages":{"items":{"$ref":"#/components/schemas/PackageItem"},"type":"array","maxItems":50,"minItems":1,"title":"Packages","description":"List of package name+version pairs to check against known vulnerability advisories. Max 50 per request."}},"type":"object","required":["packages"],"title":"DependenciesInput"},"DependenciesResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"findings":{"items":{"$ref":"#/components/schemas/DepFinding"},"type":"array","title":"Findings"},"total":{"type":"integer","title":"Total","description":"Total CVE findings across processed packages (not the input package count).","default":0},"processed":{"type":"integer","title":"Processed","description":"Count of packages actually scanned. Equal to input count unless dynamic-budget partial-fill kicked in. Field name aligns with the other 4 bulk endpoints (atlas/cve/ioc/domain).","default":0},"skipped_due_to_rate_limit":{"items":{"type":"string"},"type":"array","title":"Skipped Due To Rate Limit","description":"Package names that were not scanned because the caller's remaining hourly quota was smaller than the input list. Empty when full budget was available."},"by_severity":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Severity"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","title":"DependenciesResponse"},"DisposableResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"email":{"type":"string","title":"Email","description":"Echoed input email (local-part preserved; domain lowercased)."},"domain":{"type":"string","title":"Domain","description":"Lowercased domain extracted from the email's right-of-@."},"disposable":{"type":"boolean","title":"Disposable","description":"True when the domain matches the disposable-provider database.","default":false},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Disposable-provider name when known (e.g. 'mailinator', 'tempmail.com'). Null when not disposable."},"mx_disposable":{"type":"boolean","title":"Mx Disposable","description":"True when the domain's MX records point to a known disposable mail host (catches custom domains fronting disposable backends).","default":false},"risk_level":{"type":"string","enum":["low","medium","high"],"title":"Risk Level","description":"Combined risk band. 'high' = domain is on the disposable list; 'medium' = MX points to a disposable backend but the domain itself is not listed; 'low' = neither match (legitimate).","default":"low"},"mx_records":{"items":{"$ref":"#/components/schemas/MxRecord"},"type":"array","title":"Mx Records","description":"Resolved MX records for the email's domain (priority + host)."},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary including risk_level + provider hint.","default":""}},"type":"object","required":["email","domain"],"title":"DisposableResponse"},"DkimPosture":{"properties":{"verified_selectors":{"items":{"type":"string"},"type":"array","title":"Verified Selectors"},"status":{"type":"string","enum":["verified","unverifiable"],"title":"Status","description":"Verification status"},"tested_selectors":{"items":{"type":"string"},"type":"array","title":"Tested Selectors","description":"All selectors probed"},"findings":{"items":{"$ref":"#/components/schemas/Finding"},"type":"array","title":"Findings"}},"type":"object","required":["status"],"title":"DkimPosture","description":"DKIM posture — selector discovery and verification."},"DmarcPosture":{"properties":{"present":{"type":"boolean","title":"Present","description":"DMARC record exists"},"record":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Record"},"policy":{"anyOf":[{"type":"string","enum":["none","quarantine","reject"]},{"type":"null"}],"title":"Policy"},"subdomain_policy":{"anyOf":[{"type":"string","enum":["none","quarantine","reject"]},{"type":"null"}],"title":"Subdomain Policy"},"pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pct","description":"Rollout percentage"},"aspf":{"anyOf":[{"type":"string","enum":["s","r"]},{"type":"null"}],"title":"Aspf","description":"SPF alignment mode"},"adkim":{"anyOf":[{"type":"string","enum":["s","r"]},{"type":"null"}],"title":"Adkim","description":"DKIM alignment mode"},"rua_uris":{"items":{"type":"string"},"type":"array","title":"Rua Uris","description":"Aggregate report URIs"},"ruf_uris":{"items":{"type":"string"},"type":"array","title":"Ruf Uris","description":"Forensic report URIs"},"fo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fo","description":"Failure reporting options"},"findings":{"items":{"$ref":"#/components/schemas/Finding"},"type":"array","title":"Findings"}},"type":"object","required":["present"],"title":"DmarcPosture","description":"DMARC posture analysis."},"DnsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (lowercased, no scheme)."},"records":{"$ref":"#/components/schemas/DomainDnsInfo","description":"DNS records keyed by type (a, aaaa, mx, ns, txt, cname, soa). Keys are omitted (not null) when the lookup for that type fails. Same shape as DomainReportResponse.dns."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"One-line human-readable record summary (e.g. 'A, MX, TXT records for example.com')."}},"type":"object","required":["domain","records"],"title":"DnsResponse"},"DomainDnsInfo":{"properties":{"a":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"A","description":"A records (IPv4 addresses)."},"aaaa":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Aaaa","description":"AAAA records (IPv6 addresses)."},"mx":{"anyOf":[{"items":{"$ref":"#/components/schemas/MxDnsRecord"},"type":"array"},{"type":"null"}],"title":"Mx","description":"MX records as {priority, host} list."},"ns":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Ns","description":"NS records (nameserver hostnames)."},"txt":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Txt","description":"TXT records. By default in domain_report, filtered to security-relevant entries (SPF v=spf, DMARC v=DMARC, DKIM v=DKIM, MTA-STS v=STSv, TLS-RPT v=TLSRPTv). Pass ?include_all_txt=true to return every TXT including vendor verification strings."},"total_txt_records":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Txt Records","description":"Honest pre-filter TXT record count. Always emitted (domain_report, audit_domain, /v1/dns). Equals len(txt) when include_all_txt=true or on /v1/dns/{domain} (raw, unfiltered). 0 when no TXT records exist. Null only when the field is absent (older cached entries)."},"cname":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cname","description":"CNAME records."},"soa":{"anyOf":[{"$ref":"#/components/schemas/SoaInfo"},{"type":"null"}],"description":"SOA record (zone authority)."}},"additionalProperties":true,"type":"object","title":"DomainDnsInfo","description":"DNS records per type. Keys are omitted (not null) when the lookup for that type fails."},"DomainReportResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed, lowercased)."},"dns":{"anyOf":[{"$ref":"#/components/schemas/DomainDnsInfo"},{"type":"null"}],"description":"Forward DNS record set (A/AAAA/MX/NS/TXT/CNAME/SOA). Empty dict when all lookups fail."},"reverse_dns":{"anyOf":[{"$ref":"#/components/schemas/ReverseDnsInfo"},{"type":"null"}],"description":"Reverse-DNS resolution of the domain's primary IPv4 (PTR + shared-hosting signal)."},"whois":{"anyOf":[{"$ref":"#/components/schemas/WhoisInfoEmbedded"},{"type":"null"}],"description":"WHOIS extract (registrar, dates, nameservers, EPP status). Skipped in lite mode. Error branch populates `error`."},"ssl":{"anyOf":[{"$ref":"#/components/schemas/SslInfoEmbedded"},{"type":"null"}],"description":"SSL/TLS certificate subset (CN, issuer, validity, grade). Full shape at top-level /v1/ssl/{domain}."},"subdomains":{"anyOf":[{"$ref":"#/components/schemas/SubdomainsInfo"},{"type":"null"}],"description":"Subdomain enumeration (wordlist + crt.sh). Skipped in lite mode (returns {subdomains:[], count:0})."},"certificates":{"anyOf":[{"$ref":"#/components/schemas/CertificatesInfo"},{"type":"null"}],"description":"Certificate transparency log entries from crt.sh. Skipped in lite mode."},"email_security":{"anyOf":[{"$ref":"#/components/schemas/EmailSecurityInfo"},{"type":"null"}],"description":"SPF/DMARC/DKIM posture of the domain (email authentication grade)."},"waf":{"anyOf":[{"$ref":"#/components/schemas/WafInfo"},{"type":"null"}],"description":"WAF detection from live response headers (Cloudflare, AWS CloudFront, Akamai, Sucuri, etc.)."},"threat":{"anyOf":[{"$ref":"#/components/schemas/ThreatInfo"},{"type":"null"}],"description":"URLhaus threat intelligence for the domain (malware / phishing URL listings). Skipped in lite mode."},"risk":{"anyOf":[{"$ref":"#/components/schemas/RiskInfo"},{"type":"null"}],"description":"Composite risk scoring (0-100) with per-factor breakdown — drives the top-level risk_score alias."},"reputation":{"anyOf":[{"$ref":"#/components/schemas/DomainReputationInfo"},{"type":"null"}],"description":"IP-level reputation of the domain's resolved A record. Absent in lite mode AND when no A record resolves. On Free tier inner blocks carry {status:'pro_only'} stubs (agents should not treat as clean)."},"summary":{"type":"string","title":"Summary","description":"One-line human summary aggregating IP, grade, WAF, and subdomain count.","default":""},"risk_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Risk Score","description":"DEPRECATED — use `risk.score` instead. Top-level alias for risk.score, retained for backward compatibility. Will be removed in v2.0.0 (Sunset: 2026-09-01). Routes that emit DomainReportResponse return RFC 8594 `Deprecation: true` + `Sunset` headers.","readOnly":true}},"type":"object","required":["domain","risk_score"],"title":"DomainReportResponse"},"DomainReputationInfo":{"properties":{"abuseipdb":{"anyOf":[{"$ref":"#/components/schemas/AbuseIpdbInfo"},{"type":"null"}],"description":"AbuseIPDB enrichment for the domain's resolved IP. Pro tier only — free tier returns {status:'pro_only', reason, upgrade_url} stub."},"shodan":{"anyOf":[{"$ref":"#/components/schemas/ShodanRepInfo"},{"type":"null"}],"description":"Shodan enrichment for the domain's resolved IP. Pro tier only — free tier returns {status:'pro_only', reason, upgrade_url} stub."}},"additionalProperties":true,"type":"object","title":"DomainReputationInfo","description":"Reputation block inside DomainReportResponse (IP-level enrichment of the resolved A record).\n\nDiffers from IpLookupResponse.reputation: no firehol block here (FireHOL is IP-only)."},"EmailMxResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"mx_records":{"items":{"$ref":"#/components/schemas/MxRecord"},"type":"array","title":"Mx Records"},"mail_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mail Provider"},"email_security":{"$ref":"#/components/schemas/EmailSecurityDetail"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain"],"title":"EmailMxResponse"},"EmailSecurityDetail":{"properties":{"spf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spf"},"dmarc":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dmarc"},"dkim_selectors":{"items":{"type":"string"},"type":"array","title":"Dkim Selectors"},"dkim_status":{"anyOf":[{"type":"string","enum":["verified","unverifiable"]},{"type":"null"}],"title":"Dkim Status","description":"'verified' when at least one DKIM selector responded; 'unverifiable' when none of the probed common/date-based selectors matched. Custom selectors cannot be discovered without prior knowledge."},"grade":{"type":"string","title":"Grade","default":"F"},"issues":{"items":{"type":"string"},"type":"array","title":"Issues"}},"type":"object","title":"EmailSecurityDetail"},"EmailSecurityInfo":{"properties":{"spf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spf","description":"SPF record string (v=spf1 ...). Null when no SPF is published."},"dmarc":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dmarc","description":"DMARC record string (v=DMARC1; p=...; ...). Null when no DMARC record is published at _dmarc.<domain>."},"dkim_selectors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dkim Selectors","description":"DKIM selectors that responded to probing (e.g. ['google', 'selector1']). Empty when none found."},"dkim_status":{"anyOf":[{"type":"string","enum":["verified","unverifiable"]},{"type":"null"}],"title":"Dkim Status","description":"Honest evidence label for DKIM. 'verified' when at least one selector responded (see dkim_selectors). 'unverifiable' when no probed selector matched — DKIM keys live at arbitrary operator-chosen selector names, so absence under common+date-based probes does not prove absence. Grade does not penalize 'unverifiable'."},"grade":{"anyOf":[{"type":"string","enum":["A","B","C","F"]},{"type":"null"}],"title":"Grade","description":"Email-auth grade. When DKIM is verified: A=SPF+DMARC+DKIM, B=2 of 3, C=1 of 3. When DKIM is unverifiable: A=SPF+DMARC, B=one of SPF/DMARC, F=neither — DKIM absence is not penalized because it cannot be proven without selector knowledge."},"issues":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Issues","description":"Human-readable issues (missing SPF, weak DMARC policy, etc.)."}},"additionalProperties":true,"type":"object","title":"EmailSecurityInfo"},"EmailSecurityPostureResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Domain analyzed"},"spf":{"$ref":"#/components/schemas/SpfPosture","description":"SPF posture"},"dmarc":{"$ref":"#/components/schemas/DmarcPosture","description":"DMARC posture"},"dkim":{"$ref":"#/components/schemas/DkimPosture","description":"DKIM posture"},"posture_score":{"type":"integer","title":"Posture Score","description":"Score 0-100"},"posture_grade":{"type":"string","enum":["A+","A","B","C","D","F"],"title":"Posture Grade","description":"Grade A+-F"},"all_findings":{"items":{"$ref":"#/components/schemas/Finding"},"type":"array","title":"All Findings","description":"Flattened findings"},"summary":{"type":"string","title":"Summary","description":"Summary"}},"type":"object","required":["domain","spf","dmarc","dkim","posture_score","posture_grade","all_findings","summary"],"title":"EmailSecurityPostureResponse","description":"Email authentication posture: SPF + DMARC + DKIM with score and findings."},"EmailVerifyResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"email":{"type":"string","title":"Email","description":"Echo of the input email (lowercased, control-chars stripped)."},"domain":{"type":"string","title":"Domain","description":"The domain part (after `@`)."},"syntax_valid":{"type":"boolean","title":"Syntax Valid","description":"True iff the email passes the same RFC-aware regex used by /v1/email/disposable."},"mx_records":{"items":{"$ref":"#/components/schemas/MxDnsRecord"},"type":"array","title":"Mx Records","description":"MX records for the domain, sorted by priority. Empty list = no MX = mail cannot be delivered."},"disposable":{"type":"boolean","title":"Disposable","description":"True iff the domain matches our disposable-provider database OR a known disposable MX host.","default":false},"disposable_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Disposable Provider","description":"Name of the disposable provider when `disposable=true` (e.g. 'Mailinator'). Null otherwise."},"role_address":{"type":"boolean","title":"Role Address","description":"True iff the local-part is a generic role address (admin@, info@, support@, etc.) — not a specific person.","default":false},"role_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role Type","description":"The role keyword when role_address=true (e.g. 'admin', 'noreply'). Null otherwise."},"free_provider":{"type":"boolean","title":"Free Provider","description":"True iff the domain is a known consumer-mailbox provider (gmail/outlook/yahoo/proton/icloud). B2B detection signal.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["email","domain","syntax_valid"],"title":"EmailVerifyResponse","description":"Combined email validation: syntax + MX + disposable + role + free-provider.\n\nWhat we DO NOT do: SMTP `RCPT TO` deliverability probing. Hunter.io-style\nmailbox-existence checks are an ethical grey area (mailbox enumeration +\nHetzner ToS risk on unsolicited SMTP from datacenter IPs). Use Hunter.io /\nNeverBounce / ZeroBounce when you need that specific signal."},"EpssInfo":{"properties":{"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score","description":"EPSS probability (0.0-1.0) that this CVE will be exploited in the next 30 days."},"percentile":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Percentile","description":"EPSS percentile rank (0.0-100.0) relative to all scored CVEs; higher = more at-risk."}},"type":"object","title":"EpssInfo"},"Exploit":{"properties":{"edb_id":{"type":"integer","title":"Edb Id"},"cve_id":{"type":"string","title":"Cve Id"},"date_published":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Published"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"platform":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform"},"url":{"type":"string","title":"Url"},"verified":{"type":"boolean","title":"Verified","default":false},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["edb_id","cve_id","url"],"title":"Exploit"},"ExploitResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"cve_id":{"type":"string","title":"Cve Id"},"exploits_found":{"type":"integer","title":"Exploits Found","default":0},"sources":{"$ref":"#/components/schemas/ExploitSources"},"has_public_exploit":{"type":"boolean","title":"Has Public Exploit","default":false},"exploits":{"items":{"$ref":"#/components/schemas/Exploit"},"type":"array","title":"Exploits"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["cve_id"],"title":"ExploitResponse"},"ExploitSources":{"properties":{"github":{"$ref":"#/components/schemas/GithubExploitSource"},"shodan_refs":{"$ref":"#/components/schemas/ShodanRefSource"}},"type":"object","title":"ExploitSources"},"FeodoSource":{"properties":{"found":{"type":"boolean","title":"Found","description":"True when the IP appears on the Feodo Tracker C2 blocklist."},"malware":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Malware","description":"Malware family attributed by Feodo (e.g. 'Emotet'). Null when found=False."},"first_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen","description":"ISO timestamp of first Feodo observation. Null when found=False."},"last_online":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Online","description":"ISO timestamp the C2 was last seen online. Null when found=False."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"C2 lifecycle status per Feodo (e.g. 'online', 'offline'). Null when found=False."}},"type":"object","required":["found"],"title":"FeodoSource","description":"Feodo Tracker C2 blocklist entry inside IocResponse.sources.feodo (IP only)."},"Finding":{"properties":{"check":{"type":"string","title":"Check","description":"What was checked"},"status":{"type":"string","enum":["pass","warn","fail"],"title":"Status","description":"Outcome"},"severity":{"type":"string","enum":["critical","high","medium","low"],"title":"Severity","description":"Severity"},"description":{"type":"string","title":"Description","description":"Finding description"},"fix_hint":{"type":"string","title":"Fix Hint","description":"Remediation hint"}},"type":"object","required":["check","status","severity","description","fix_hint"],"title":"Finding","description":"Discrete audit finding."},"FireholInfo":{"properties":{"status":{"type":"string","enum":["ok","skipped","unavailable"],"title":"Status","description":"'ok' = trie lookup succeeded; 'skipped' = private/reserved/loopback/link-local IP, not meaningful to check; 'unavailable' = FireHOL feed could not be fetched (be honest with agent)."},"listed":{"type":"boolean","title":"Listed","description":"True if the IP matches any range in firehol_level1 (known-bad aggregated blocklist).","default":false},"lists_matched":{"items":{"type":"string"},"type":"array","title":"Lists Matched","description":"List identifiers matched. Currently ['firehol_level1'] when listed, else empty."}},"type":"object","required":["status"],"title":"FireholInfo","description":"FireHOL level1 blocklist check (Free tier and Pro)."},"GeoAuditResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed)."},"fetched_url":{"type":"string","title":"Fetched Url","description":"Final URL we fetched (post-redirects)."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status returned by the homepage fetch."},"llms_txt_present":{"type":"boolean","title":"Llms Txt Present","description":"True if https://<domain>/llms.txt returns 200 with a non-empty body (emerging AI-context standard).","default":false},"ai_crawlers_total":{"type":"integer","title":"Ai Crawlers Total","description":"Number of AI crawler user-agents checked against robots.txt.","default":0},"ai_crawlers_allowed":{"type":"integer","title":"Ai Crawlers Allowed","description":"How many of those AI crawlers are permitted to fetch path '/'.","default":0},"ai_crawlers_blocked":{"items":{"type":"string"},"type":"array","title":"Ai Crawlers Blocked","description":"AI crawler tokens explicitly disallowed by robots.txt (e.g. 'GPTBot'). Blocking = invisible to that AI surface."},"schema_types":{"items":{"type":"string"},"type":"array","title":"Schema Types","description":"Distinct valuable schema.org @types found in JSON-LD (Organization, Product, FAQPage, SoftwareApplication, WebSite, BreadcrumbList)."},"client_side_rendered":{"type":"boolean","title":"Client Side Rendered","description":"True if the homepage appears to be a client-only SPA (framework marker + near-empty server HTML) — AI crawlers may see no content.","default":false},"render_framework":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Render Framework","description":"SPA framework marker detected in the served HTML, if any (informational; one of a fixed known set)."},"has_canonical":{"type":"boolean","title":"Has Canonical","description":"True if a `<link rel='canonical'>` is present.","default":false},"og_tag_count":{"type":"integer","title":"Og Tag Count","description":"Number of `<meta property='og:*'>` tags (capped at 50).","default":0},"sitemap_count":{"type":"integer","title":"Sitemap Count","description":"Number of Sitemap: entries declared in robots.txt.","default":0},"h1_count":{"type":"integer","title":"H1 Count","description":"Total `<h1>` tags found.","default":0},"h2_count":{"type":"integer","title":"H2 Count","description":"Total `<h2>` tags found.","default":0},"comparison_content":{"type":"boolean","title":"Comparison Content","description":"True if the page shows competitor-comparison signals ('vs', 'versus', 'compare', 'alternative').","default":false},"score":{"type":"integer","title":"Score","description":"Composite 0-100 GEO-readiness score across 7 weighted rules."},"missing_signals":{"items":{"type":"string"},"type":"array","title":"Missing Signals","description":"Rule-IDs that did NOT earn full points. Subset of: llms_txt_missing, ai_crawlers_blocked, schema_org_missing, schema_org_sparse, client_side_rendered, og_missing, canonical_missing, sitemap_missing, h1_not_single, no_h2_structure, comparison_content_missing."},"cache_respected":{"type":"boolean","title":"Cache Respected","description":"True if we wrote the result to our cache. False when the target sent `Cache-Control: no-store`/`private`.","default":true},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["domain","fetched_url","status_code","score"],"title":"GeoAuditResponse","description":"Deterministic GEO / AI-visibility readiness audit of a domain's homepage (0-100).\n\nAnswers \"can AI assistants discover, crawl, and recommend this site?\"\nusing structural signals ONLY — NO LLM is queried. AI-native cousin\nof `seo_audit`. Same ethical floor: robots.txt honoured (Disallow `/`\nfor our UA → 403, no fetch).\n\nScore = 7 weighted rules: llms.txt present (15), AI-crawler robots\naccess (25), schema.org @type coverage (20), server-side rendering\n(15), discovery signals OG/canonical/sitemap (10), semantic headings\n(10), comparison content (5). `missing_signals` names each gap."},"GhsaAdvisory":{"properties":{"ghsa_id":{"type":"string","title":"Ghsa Id","default":""},"summary":{"type":"string","title":"Summary","default":""},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"published_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published At"},"references":{"items":{"type":"string"},"type":"array","title":"References"}},"type":"object","title":"GhsaAdvisory"},"GithubExploitSource":{"properties":{"found":{"type":"boolean","title":"Found","default":false},"count":{"type":"integer","title":"Count","default":0},"advisories":{"items":{"$ref":"#/components/schemas/GhsaAdvisory"},"type":"array","title":"Advisories"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"GithubExploitSource"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HashResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"hash":{"type":"string","title":"Hash"},"hash_type":{"type":"string","title":"Hash Type"},"found":{"type":"boolean","title":"Found","default":false},"malware_family":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Malware Family"},"file_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Type"},"file_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Size"},"first_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"file_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Name"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["hash","hash_type"],"title":"HashResponse"},"HeaderFinding":{"properties":{"header":{"type":"string","title":"Header","description":"Canonical header name as defined by the ruleset (e.g. 'Strict-Transport-Security', 'Content-Security-Policy')."},"severity":{"type":"string","enum":["high","medium","low"],"title":"Severity","description":"Impact weight assigned by the ruleset: 'high' (25 pts), 'medium' (15 pts), 'low' (10 pts). Drives the overall score/grade — missing a 'high' header costs more than missing a 'low' one."},"present":{"type":"boolean","title":"Present","description":"True when the response sent this header at all (regardless of whether the value is valid)."},"valid":{"type":"boolean","title":"Valid","description":"Value-level validation result. True when the header is present AND its value passes the header-specific validator (e.g. HSTS max-age >= 1 year + includeSubDomains; CSP has no wildcard source in script-src). True also when the header is present but no validator exists for it. False when the header is absent, or present-but-invalid. Inspect `issues` for the specific reasons a present-but-invalid header failed.","default":false},"value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Value","description":"Raw header value as sent by the origin, when the header is present AND a validator exists for it. Null when the header is absent, or when it's present but no validator applies to it. By default the value is capped at the first 500 chars (CSP headers can exceed 4 KB); inspect total_value_length to see if truncation occurred and refetch with include=full to restore the full value."},"total_value_length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Value Length","description":"Honest pre-truncation char length of the raw header value. Only emitted when the value was actually truncated (raw length > 500). Null when no truncation occurred, when no validator applies, or when the header is absent."},"issues":{"items":{"type":"string"},"type":"array","title":"Issues","description":"Machine-readable issue codes emitted by the validator for present-but-invalid headers (e.g. 'hsts_max_age_too_short', 'csp_wildcard_script_src', 'xfo_allowall'). Empty when the header is absent, valid, or has no validator."},"description":{"type":"string","title":"Description","description":"Human-readable explanation of what this header protects against.","default":""},"remediation":{"type":"string","title":"Remediation","description":"Concrete recommended header value or configuration snippet.","default":""},"reference":{"type":"string","title":"Reference","description":"URL to authoritative spec/documentation (MDN, OWASP, RFC).","default":""}},"type":"object","required":["header","severity","present"],"title":"HeaderFinding"},"HeadersInput":{"properties":{"headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Headers","description":"HTTP response header name-value pairs to validate against best practices. Maximum 50 headers per request. Header names are case-insensitive; include only security-relevant headers (CSP, HSTS, X-Frame-Options, etc.) — non-security headers are ignored."}},"type":"object","required":["headers"],"title":"HeadersInput"},"IocResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"indicator":{"type":"string","title":"Indicator","description":"Echoed input indicator (sanitized; control chars stripped)."},"type":{"type":"string","enum":["ip","domain","url","hash","unknown"],"title":"Type","description":"Auto-detected indicator type. 'unknown' is rejected at route level (400)."},"threat_level":{"type":"string","enum":["none","low","medium","high"],"title":"Threat Level","description":"Heuristic threat tier from cross-source agreement. 'high' = >=2 sources flagged; 'medium' = 1 source flagged; 'none' = no source flagged. 'low' is a soft cap applied when the only flag came from a ThreatFox test/demo honeypot tag.","default":"none"},"sources":{"$ref":"#/components/schemas/IocSourcesInfo","description":"Per-source lookup results. See IocSourcesInfo for which sources apply per indicator type."},"summary":{"type":"string","title":"Summary","description":"One-line human summary aggregating threat indicators across sources.","default":""}},"type":"object","required":["indicator","type"],"title":"IocResponse"},"IocSourcesInfo":{"properties":{"threatfox":{"anyOf":[{"$ref":"#/components/schemas/ThreatFoxSource"},{"type":"null"}],"description":"ThreatFox lookup result. Always queried."},"feodo":{"anyOf":[{"$ref":"#/components/schemas/FeodoSource"},{"type":"null"}],"description":"Feodo Tracker C2 blocklist lookup. IP indicators only."},"urlhaus":{"anyOf":[{"$ref":"#/components/schemas/UrlhausSource"},{"type":"null"}],"description":"URLhaus URL/host match. IP/domain/URL indicators."},"tor":{"anyOf":[{"$ref":"#/components/schemas/TorSource"},{"type":"null"}],"description":"Tor exit list membership. IP indicators only."}},"type":"object","title":"IocSourcesInfo","description":"Per-source lookup results inside IocResponse. Keys present depend on indicator type.\n\n- hash → only `threatfox` (Feodo and URLhaus do not index hashes).\n- ip → `threatfox` + `feodo` + `urlhaus` + `tor`.\n- domain / url → `threatfox` + `urlhaus`."},"IpEnrichmentInfo":{"properties":{"ports":{"items":{"type":"integer"},"type":"array","title":"Ports","description":"Open ports observed by Shodan InternetDB. Empty on upstream failure (treat as 'no data', not 'closed')."},"hostnames":{"items":{"type":"string"},"type":"array","title":"Hostnames","description":"Hostnames Shodan InternetDB has observed pointing to this IP."},"vulns":{"items":{"$ref":"#/components/schemas/VulnInfo"},"type":"array","title":"Vulns","description":"CVEs Shodan InternetDB has associated with banners on this IP, enriched with severity + cvss_v3 from local cve.db (Phase 2 IP enrichment, v1.16.0 BREAKING). Pre-1.16 this was a flat list[str] of CVE IDs. Unknown CVEs emit severity='UNKNOWN'."},"cpes":{"items":{"type":"string"},"type":"array","title":"Cpes","description":"CPE 2.3 strings for services detected on this IP per Shodan InternetDB."},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Shodan InternetDB classification tags (e.g. 'cdn', 'cloud', 'vpn', 'tor', 'self-signed')."},"internetdb_status":{"anyOf":[{"type":"string","enum":["ok","error"]},{"type":"null"}],"title":"Internetdb Status","description":"Outcome of the InternetDB fetch. 'error' indicates upstream failure; absent on cached/legacy paths."}},"type":"object","title":"IpEnrichmentInfo","description":"Shodan InternetDB enrichment subset (free, no API key) embedded in /v1/threat_report.\n\nMirrors the {ports, hostnames, vulns, cpes, tags} block at the top of ip_lookup,\nplus an internetdb_status field that surfaces the upstream fetch outcome —\nextracted as a sub-model so MCP clients see a typed schema instead of an opaque dict slot."},"IpLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"ip":{"type":"string","title":"Ip","description":"Queried IP address (IPv4 or IPv6, echoed back verbatim)."},"ptr":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ptr","description":"Reverse-DNS PTR record. Null when no PTR is published."},"asn":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Asn","description":"Autonomous System Number from RIPE Stat network-info (e.g. 13335 for Cloudflare)."},"asn_name":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Asn Name","description":"Human-readable AS name from RIPE Stat as-overview (e.g. 'CLOUDFLARENET')."},"country":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Country","description":"ISO 3166-1 alpha-2 country code from RIPE Stat rir-stats-country (RIR-allocated)."},"ports":{"items":{"type":"integer"},"type":"array","title":"Ports","description":"Open ports observed by Shodan InternetDB (free, no API key; superseded by reputation.shodan.ports on Pro)."},"hostnames":{"items":{"type":"string"},"type":"array","title":"Hostnames","description":"Hostnames observed pointing to this IP per Shodan InternetDB."},"vulns":{"items":{"$ref":"#/components/schemas/VulnInfo"},"type":"array","title":"Vulns","description":"CVEs Shodan InternetDB has associated with banners on this IP, enriched with severity + cvss_v3 from local cve.db (Phase 2 IP enrichment, v1.16.0 BREAKING). Pre-1.16 this was a flat list[str] of CVE IDs. Order is preserved from Shodan (meaningful — Shodan ranks confidence). Unknown CVEs emit severity='UNKNOWN'; do NOT infer 'benign' from UNKNOWN."},"cpes":{"items":{"type":"string"},"type":"array","title":"Cpes","description":"CPE 2.3 strings for services detected on this IP per Shodan InternetDB."},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Shodan InternetDB classification tags (e.g. 'cdn', 'cloud', 'vpn', 'tor', 'self-signed')."},"reputation":{"anyOf":[{"$ref":"#/components/schemas/ReputationInfo"},{"type":"null"}],"description":"Multi-source reputation. Free tier: firehol populated, abuseipdb/shodan return status='pro_only' upsell stubs. Pro tier: all three live."},"cloud_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cloud Provider","description":"Cloud provider name resolved via two-tier detection: (1) published cloud CIDR ranges (AWS/GCP/Cloudflare), (2) ASN-to-provider map fallback for anycast/public-service IPs outside published ranges (e.g. 8.8.8.8 → AS15169 → 'Google'). Null when neither matches. Always present in response (route emits null-explicit so agents can disambiguate 'not detected' from 'field absent')."},"is_datacenter":{"type":"boolean","title":"Is Datacenter","description":"True if IP is hosted on a known datacenter / cloud provider. Detection: (1) cloud_provider populated (CIDR or ASN map hit covering AWS/GCP/Cloudflare/DigitalOcean/Hetzner/OVH/Linode/Vultr/Microsoft Azure), (2) ASN in tier-1 datacenter set (adds Oracle/Alibaba/Tencent on top of the cloud_provider map). Use for Nuclei matchers + bug-bounty triage where datacenter targets warrant different scan policy than residential IPs. Always present — never null.","default":false},"tor_exit":{"type":"boolean","title":"Tor Exit","description":"True if IP appears in the Tor Project's exit node list. False when not listed or when the upstream list fetch failed (check verdict.sources_unavailable for 'tor' to distinguish). Always present in response — never null.","default":false},"risk_score":{"type":"integer","title":"Risk Score","description":"Composite 0-100 risk score (v1.17.0 formula). Additive components: ports (10 * min(count, 5) = 0-50), tor_exit (+30), firehol.listed (+20), AbuseIPDB confidence (round(15 * score / 100) = 0-15), is_datacenter (+10), known vulns (5 * min(count, 4) = 0-20). Datacenter membership now adds risk (was a -10 trust bonus pre-1.17). Use severity_label for thresholding.","default":0},"severity_label":{"type":"string","enum":["low","medium","high","critical"],"title":"Severity Label","description":"Coarse risk band derived from risk_score (>=75 critical, >=50 high, >=25 medium, else low). Use this for Nuclei matchers and MCP agent triage when you don't want to re-implement the threshold logic; risk_score is the canonical numeric source.","default":"low"},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary built from IP, PTR, ASN, country, ports, vulns.","default":""}},"type":"object","required":["ip"],"title":"IpLookupResponse"},"KevDetailResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"cve_id":{"type":"string","title":"Cve Id","description":"Canonical CVE identifier, e.g. 'CVE-2021-44228'."},"in_kev":{"type":"boolean","title":"In Kev","description":"Always True for this endpoint — 404 is returned when the CVE is not in the KEV catalog.","default":true},"date_added":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Added","description":"ISO 8601 date CISA added this CVE to the Known Exploited Vulnerabilities catalog."},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date","description":"Federal patch deadline (ISO 8601). Null for older entries from before CISA enforced remediation due dates (BOD 22-01, Nov 2021)."},"required_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Required Action","description":"CISA-specified remediation action text, e.g. 'Apply updates per vendor instructions'."},"known_ransomware_use":{"type":"boolean","title":"Known Ransomware Use","description":"True when CISA has linked this CVE to a known ransomware campaign. Derived from CISA's 'knownRansomwareCampaignUse=Known' field.","default":false},"vendor_project":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor Project","description":"Vendor or project name as published by CISA, e.g. 'Apache', 'Microsoft', 'Atlassian'."},"product":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product","description":"Affected product name as published by CISA, e.g. 'Log4j2', 'Exchange Server'."},"vulnerability_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vulnerability Name","description":"Short common name of the vulnerability when one is assigned, e.g. 'Log4Shell', 'ProxyShell'."},"short_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Short Description","description":"CISA's one-sentence summary of the vulnerability."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Reference URLs published by CISA, separated by '; '."},"cwes":{"items":{"type":"string"},"type":"array","maxItems":100,"title":"Cwes","description":"CWE identifiers CISA reports for this CVE. May differ from the NVD-assigned CWE. Call cwe_lookup with each entry to fetch weakness category, mitigations, and parent/child chain."},"date_updated":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Updated","description":"ISO 8601 timestamp from KEV catalog 'dateUpdated' field — when CISA last revised this entry's metadata."},"date_removed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Removed","description":"ISO 8601 timestamp when CISA removed this CVE from the KEV catalog. Null while still active."},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO 8601 timestamp of our last KEV sync that touched this row (DB-side freshness, distinct from date_updated)."}},"additionalProperties":true,"type":"object","required":["cve_id"],"title":"KevDetailResponse","description":"Full CISA KEV catalog record for a single CVE.\n\nText fields (required_action, notes, vulnerability_name, short_description) are\nsourced verbatim from CISA's official feed and JSON-encoded — safe for\nJSON consumers, but downstream callers that render into HTML must apply their\nown escaping.\n\n`extra=\"allow\"` is set for forward-compat (Tier 2 audit pattern, Session 171).\nOnly PivotHint objects in `next_calls` and CISA-sourced DB columns appear in extras."},"KevInfo":{"properties":{"in_kev":{"type":"boolean","title":"In Kev","description":"True when CISA has confirmed this CVE is being actively exploited in the wild.","default":false},"date_added":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Added","description":"ISO 8601 date this CVE was added to CISA's Known Exploited Vulnerabilities catalog."},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date","description":"Federal patch deadline (ISO 8601). Null for entries from before BOD 22-01 (Nov 2021)."},"required_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Required Action","description":"CISA-specified remediation action text (mirrors kev_detail.required_action)."},"known_ransomware_use":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Known Ransomware Use","description":"True when CISA links this CVE to a known ransomware campaign. Null when CVE is not in KEV."},"vendor_project":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor Project","description":"Vendor or project name as published by CISA (mirrors kev_detail.vendor_project)."},"product":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product","description":"Affected product name as published by CISA (mirrors kev_detail.product)."},"vulnerability_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vulnerability Name","description":"Short common name when CISA assigns one, e.g. 'Log4Shell', 'ProxyShell'."},"short_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Short Description","description":"CISA's one-sentence summary (mirrors kev_detail.short_description)."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Reference URLs published by CISA, separated by '; '."},"cwes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Cwes","description":"CWE identifiers CISA reports for this CVE (Batch 4A view-ID filtered)."},"date_removed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Removed","description":"ISO 8601 timestamp when CISA removed this CVE from the KEV catalog. Null when CVE is still active in KEV."}},"type":"object","title":"KevInfo"},"MonitorResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"is_up":{"type":"boolean","title":"Is Up"},"ssl_days_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ssl Days Remaining"},"ssl_grade":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ssl Grade"},"dns_a":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dns A"},"dns_changed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Dns Changed"},"risk_grade":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Risk Grade"},"risk_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Risk Score"},"last_full_report":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Full Report"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain","is_up"],"title":"MonitorResponse"},"MxDnsRecord":{"properties":{"priority":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Priority","description":"MX preference (lower = higher priority)."},"host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host","description":"MX hostname (trailing dot stripped)."}},"type":"object","title":"MxDnsRecord","description":"Single MX record embedded inside DomainReportResponse.dns.mx."},"MxRecord":{"properties":{"priority":{"type":"integer","title":"Priority"},"host":{"type":"string","title":"Host"}},"type":"object","required":["priority","host"],"title":"MxRecord"},"PackageItem":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name","description":"Package name as published in its ecosystem registry (e.g. 'requests', 'lodash', 'log4j-core')."},"version":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Version","description":"Optional exact version string. Omit to check the package itself for advisories without version filtering."}},"type":"object","required":["name"],"title":"PackageItem"},"PasswordResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"hash_prefix":{"type":"string","title":"Hash Prefix","description":"First 5 chars of the SHA-1 hash (the only data sent upstream — k-anonymity). The full hash never leaves the server."},"found":{"type":"boolean","title":"Found","description":"True when the full SHA-1 was matched in HIBP's breach corpus.","default":false},"breach_count":{"type":"integer","title":"Breach Count","description":"Number of breach corpora that contained this password. 0 when found=False.","default":0},"summary":{"type":"string","title":"Summary","description":"One-line human-readable result (e.g. 'This password appeared in 12,345 data breaches').","default":""}},"type":"object","required":["hash_prefix"],"title":"PasswordResponse"},"PhishingResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"url":{"type":"string","title":"Url"},"host":{"type":"string","title":"Host"},"is_malicious":{"type":"boolean","title":"Is Malicious","default":false},"is_stale":{"type":"boolean","title":"Is Stale","description":"True when the only URLhaus evidence is historical (host has url_count > 0 but urls_online == 0, OR exact URL match has status == 'offline'). The host or URL was once flagged but no live malware is currently being served — useful for distinguishing past compromise from active threat.","default":false},"urlhaus_host":{"$ref":"#/components/schemas/UrlhausHostDetail"},"urlhaus_url":{"$ref":"#/components/schemas/UrlhausUrlDetail"},"threat_level":{"type":"string","enum":["none","low","medium","high"],"title":"Threat Level","description":"Aggregate severity. 'high' = exact URL active AND host has live malware URLs. 'medium' = exactly one of those active. 'low' = only stale historical evidence (is_stale=True). 'none' = no URLhaus listing for either.","default":"none"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["url","host"],"title":"PhishingResponse"},"PhoneFormat":{"properties":{"e164":{"type":"string","title":"E164","description":"E.164 canonical format, e.g. '+14155552671'. Empty when parse fails.","default":""},"international":{"type":"string","title":"International","description":"Human-readable international format, e.g. '+1 415-555-2671'.","default":""},"national":{"type":"string","title":"National","description":"Domestic format for the number's country, e.g. '(415) 555-2671'.","default":""}},"type":"object","title":"PhoneFormat"},"PhoneLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"valid":{"type":"boolean","title":"Valid","description":"True only when phonenumbers.is_valid_number() passes (correct length, valid prefix for region).","default":false},"number":{"type":"string","title":"Number","description":"Echoed input, normalized. Prefer format.e164 for downstream lookups.","default":""},"format":{"anyOf":[{"$ref":"#/components/schemas/PhoneFormat"},{"type":"null"}],"description":"E.164, international, and national representations. Null when the number could not be parsed at all."},"country_code":{"type":"string","title":"Country Code","description":"ISO 3166-1 alpha-2 region code (e.g. 'US', 'TR'). Empty when region cannot be inferred.","default":""},"country_name":{"type":"string","title":"Country Name","description":"Full country name from libphonenumber geocoder. Empty when region cannot be inferred.","default":""},"type":{"type":"string","title":"Type","description":"Phone number type: 'mobile', 'fixed_line', 'fixed_line_or_mobile', 'voip', 'toll_free', 'premium_rate', 'shared_cost', 'personal_number', 'pager', 'uan', or 'unknown'.","default":"unknown"},"carrier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Carrier","description":"Carrier/network name from libphonenumber carrier DB. Excluded from the wire (response_model_exclude_none=True) when no carrier mapping exists for the region — inspect carrier_status to distinguish 'known' vs 'unsupported_region' (US/CA/GB and other MNP-restricted regions are commonly unsupported)."},"carrier_status":{"anyOf":[{"type":"string","enum":["known","unsupported_region"]},{"type":"null"}],"title":"Carrier Status","description":"'known' when libphonenumber returned a carrier name; 'unsupported_region' when the carrier DB has no mapping for this region (do not treat the absent carrier field as evidence the number lacks a carrier — it just means we cannot identify it). Null on invalid/unparseable input."},"timezone":{"items":{"type":"string"},"type":"array","title":"Timezone","description":"IANA timezone identifiers associated with the number's geography (e.g. ['America/Los_Angeles'])."},"summary":{"type":"string","title":"Summary","description":"One-line human summary, e.g. '+14155552671 United States mobile AT&T'.","default":""},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Parse/validation error message. Null on successful lookups."}},"type":"object","title":"PhoneLookupResponse"},"PivotHint":{"properties":{"tool":{"type":"string","enum":["cve_lookup","cve_search","cve_leading","bulk_cve_lookup","calculate_risk_score","get_cvss_details","exploit_lookup","kev_detail","cwe_lookup","subdomain_enum","ssl_check","tech_fingerprint","asn_lookup","ip_lookup","ioc_lookup","bulk_ioc_lookup","hash_lookup","threat_intel","threat_report","audit_domain","domain_report","dns_lookup","whois_lookup","wayback_lookup","scan_headers","check_headers","check_secrets","check_injection","check_dependencies","email_mx","email_security_posture","email_disposable","email_verify","robots_txt","redirect_chain","brand_assets","seo_audit","geo_audit","phone_lookup","username_lookup","password_check","phishing_check","atlas_technique_lookup","atlas_technique_search","bulk_atlas_technique_lookup","atlas_case_study_lookup","atlas_case_study_search","d3fend_defense_lookup","d3fend_defense_search","d3fend_defense_for_attack","d3fend_attack_coverage","sigma_rule_lookup","bulk_sigma_rule_lookup","tech_stack_cve_audit","contrast_scan"],"title":"Tool","description":"Canonical MCP tool name to call next. Constrained to known operation_ids in tools/list — adding a new tool here requires expanding the Literal."},"input":{"type":"string","title":"Input","description":"Suggested input value to pass to the tool — typically a CVE ID, CWE ID, domain, or IP. Pre-populated from the current response so the agent can call the next tool without re-deriving the argument."},"reason":{"type":"string","title":"Reason","description":"Short rationale (one sentence) for why this follow-up call adds value, e.g. 'Federal patch deadline + ransomware association', 'Public exploits / PoC availability'."},"params":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Params","description":"Optional extra kwargs to pass alongside `input`. Used by pivot generators when the next call benefits from a secondary parameter, e.g. {'exclude_id': 'AML.T0051'} to skip the originating technique from a sibling-tactic search. Omitted when no extra args are needed."}},"additionalProperties":true,"type":"object","required":["tool","input","reason"],"title":"PivotHint","description":"A suggested follow-up MCP tool call. Surfaced inside response.next_calls so\nLLM agents can chain related lookups without manual prompting. Each hint names\nthe tool, the input value to pass, and a short reason explaining why this\npivot adds value in the current context."},"RedirectChainResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"start_url":{"type":"string","title":"Start Url","description":"Echo of the input URL after sanitisation."},"final_url":{"type":"string","title":"Final Url","description":"The URL of the terminal (non-redirect) response, or the last redirect target reached if the chain was truncated. `_untrusted`."},"hops":{"items":{"$ref":"#/components/schemas/RedirectHop"},"type":"array","title":"Hops","description":"Ordered list of hops, one entry per HTTP request issued. hops[0].url == start_url."},"hop_count":{"type":"integer","title":"Hop Count","description":"Total fetches performed (= len(hops)). Capped at REDIRECT_MAX_HOPS=10."},"final_status":{"type":"integer","title":"Final Status","description":"HTTP status of the last hop, or 0 if the chain failed before any successful response."},"loop_detected":{"type":"boolean","title":"Loop Detected","description":"True if a hop's Location pointed back to a URL already visited (the duplicate fetch was NOT performed).","default":false},"truncated":{"type":"boolean","title":"Truncated","description":"True if the chain still had a 30x at hop_count == REDIRECT_MAX_HOPS — the next hop was NOT followed.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["start_url","final_url","hop_count","final_status"],"title":"RedirectChainResponse","description":"Manual hop-by-hop walk through HTTP redirects. SSRF-guarded at each hop."},"RedirectHop":{"properties":{"url":{"type":"string","title":"Url","description":"The URL fetched at this hop (absolute, control-chars stripped). `_untrusted` — DO NOT execute or shell-out."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status returned at this hop."},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Resolved Location header for this hop's response (absolute, against this hop's final URL). None when status is not a redirect or no Location was sent. `_untrusted`."},"latency_ms":{"type":"integer","title":"Latency Ms","description":"Round-trip time in milliseconds for this single hop fetch."}},"additionalProperties":true,"type":"object","required":["url","status_code","latency_ms"],"title":"RedirectHop","description":"Single hop in a redirect chain."},"ReferenceItem":{"properties":{"url":{"type":"string","title":"Url","description":"Reference URL (advisory, patch commit, exploit PoC, mailing list, etc.)"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"NVD reference tags: Patch, Vendor Advisory, Mailing List, Exploit, Third Party Advisory, Issue Tracking, Release Notes, Technical Description, US Government Resource, VDB Entry. Empty list for MITRE/GHSA/OSV refs (those upstream feeds don't carry tags)."},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Upstream source identifier. NVD: raw 'source' field (UUID/email of the CVE Numbering Authority); MITRE/GHSA/OSV: parser-name normalized to 'mitre'/'ghsa'/'osv'."}},"type":"object","required":["url"],"title":"ReferenceItem","description":"Structured reference object with NVD upstream tags + source provenance.\nEmitted in CveResponse.references_full when ?include_reference_tags=true."},"ReputationInfo":{"properties":{"firehol":{"anyOf":[{"$ref":"#/components/schemas/FireholInfo"},{"type":"null"}],"description":"FireHOL level1 blocklist membership. Available on Free tier."},"abuseipdb":{"anyOf":[{"$ref":"#/components/schemas/AbuseIpdbInfo"},{"type":"null"}],"description":"AbuseIPDB abuse confidence. Pro tier only — omitted from the response on Free."},"shodan":{"anyOf":[{"$ref":"#/components/schemas/ShodanRepInfo"},{"type":"null"}],"description":"Shodan full API enrichment. Pro tier only — omitted from the response on Free."},"upgrade":{"anyOf":[{"$ref":"#/components/schemas/ReputationUpgradeHint"},{"type":"null"}],"description":"Free-tier-only pointer to the Pro-only sources that were skipped."}},"type":"object","title":"ReputationInfo","description":"Multi-source IP reputation. Sources present depend on tier (Free: firehol only; Pro: all three)."},"ReputationUpgradeHint":{"properties":{"pro_only_sources":{"items":{"type":"string"},"type":"array","title":"Pro Only Sources"},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"ReputationUpgradeHint","description":"Compact pointer that replaces the verbose pro_only sub-stubs for Free tier.\n\nBug I4: previously the abuseipdb/shodan slots carried full Pydantic models\nwith every field null + a status='pro_only' marker — ~150 tokens of pure\nnegative space per Free-tier ip_lookup response. The verdict block already\nlists those sources in sources_unavailable on Free; this hint just points\ncallers at the upgrade page in one line."},"ReverseDnsInfo":{"properties":{"ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip","description":"Resolved IPv4 for the domain. Null when DNS fails or IP is private."},"ptr":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ptr","description":"PTR (reverse-DNS) hostname for the IP. Null when no PTR is published."},"shared_hosting":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Shared Hosting","description":"True when PTR hostname differs from the queried domain (shared hosting signal). Absent when PTR lookup fails."}},"additionalProperties":true,"type":"object","title":"ReverseDnsInfo"},"RiskFactor":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Factor label (e.g. 'SSL/TLS', 'Email Security', 'IP Reputation')."},"score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Score","description":"Points earned by this factor (can be negative for penalties)."},"max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max","description":"Maximum possible points for this factor."},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail","description":"Human-readable justification for the score."}},"type":"object","title":"RiskFactor"},"RiskInfo":{"properties":{"score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Score","description":"Cumulative risk score (0-100)."},"max_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Score","description":"Maximum achievable score, 100 by default. It drops by a factor's max when that signal could not be measured, so grade reflects the available signals instead of penalizing the domain for our blind spot. Observed values are 100, 95, 90, 85, 80 and 75: a crt.sh failure excludes the 10-point CT factor, an unverifiable DKIM selector trims the email factor 25->20, and wildcard DNS can exclude the 10-point subdomain factor. ALWAYS compute percentages against this field, never against a literal 100."},"grade":{"anyOf":[{"type":"string","enum":["A","B","C","D","F"]},{"type":"null"}],"title":"Grade","description":"Letter grade derived from score."},"factors":{"anyOf":[{"items":{"$ref":"#/components/schemas/RiskFactor"},"type":"array"},{"type":"null"}],"title":"Factors","description":"Per-factor scoring breakdown (typically 8-9 factors)."}},"additionalProperties":true,"type":"object","title":"RiskInfo"},"RiskScoreResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"cve_id":{"type":"string","title":"Cve Id","description":"Canonical CVE identifier, e.g. 'CVE-2021-44228'."},"score":{"type":"number","title":"Score","description":"Composite risk score in 0.0-100.0. Higher = more urgent."},"label":{"type":"string","enum":["CRITICAL","HIGH","MEDIUM","LOW"],"title":"Label","description":"Risk band derived from score (CRIT≥90, HIGH≥70, MED≥40, LOW<40)."},"urgency":{"type":"string","title":"Urgency","description":"Human-readable patch urgency (e.g. 'Patch within 24 hours')."},"has_public_poc":{"type":"boolean","title":"Has Public Poc","description":"True when a public PoC is available (ExploitDB local mirror)."},"components":{"additionalProperties":true,"type":"object","title":"Components","description":"Inputs that fed the score: cvss_v3, epss_score, in_kev, has_public_poc, weighted_breakdown ({cvss, epss, kev, poc} each scaled 0-100 * weight)."},"boosters_applied":{"items":{"type":"string"},"type":"array","title":"Boosters Applied","description":"Multiplicative boosters applied: kev_with_public_poc (*1.15), critical_severity_high_epss (*1.10), published_within_7_days (*1.05)."},"recommendation":{"type":"string","title":"Recommendation","description":"One-sentence remediation guidance."},"summary":{"type":"string","title":"Summary","description":"One-line agent-friendly summary of the verdict."}},"type":"object","required":["cve_id","score","label","urgency","has_public_poc","components","recommendation","summary"],"title":"RiskScoreResponse","description":"Composite CVE risk score (CVSS / EPSS / KEV / PoC fusion)."},"RobotsRules":{"properties":{"allow":{"items":{"type":"string"},"type":"array","title":"Allow","description":"Paths the target site explicitly Allows for this UA. Each entry is verbatim from robots.txt (`_untrusted` — DO NOT execute or shell-out)."},"disallow":{"items":{"type":"string"},"type":"array","title":"Disallow","description":"Paths the target site Disallows for this UA. Empty Disallow per spec means allow-all."},"crawl_delay":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Crawl Delay","description":"Crawl-delay seconds for this UA, if specified."}},"additionalProperties":true,"type":"object","title":"RobotsRules","description":"Per-User-agent rule block parsed from a robots.txt file."},"RobotsTxtResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed)."},"fetched_url":{"type":"string","title":"Fetched Url","description":"Final URL we fetched, e.g. https://example.com/robots.txt."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status returned by the target. 404 = no robots.txt = implicit allow-all."},"sitemaps":{"items":{"type":"string"},"type":"array","title":"Sitemaps","description":"`Sitemap:` directives (URLs). Global, not per-UA. `_untrusted` — fetch only via SSRF-safe path."},"user_agents":{"additionalProperties":{"$ref":"#/components/schemas/RobotsRules"},"type":"object","title":"User Agents","description":"Per-`User-agent:` rule blocks. Wildcard `*` is one of the keys when present."},"host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host","description":"`Host:` directive (Yandex extension), if present. `_untrusted`."},"truncated":{"type":"boolean","title":"Truncated","description":"True if the robots.txt body exceeded ROBOTS_MAX_BYTES and was truncated before parsing.","default":false},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["domain","fetched_url","status_code"],"title":"RobotsTxtResponse","description":"Parsed robots.txt for the target domain."},"ScanHeadersResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (lowercased, no scheme)."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status code returned by the live origin during the header probe.","default":0},"url":{"type":"string","title":"Url","description":"Final URL the probe landed on (after redirects).","default":""},"score":{"type":"integer","title":"Score","description":"Aggregate header-posture score (0-100) summed from per-finding severity weights.","default":0},"grade":{"type":"string","enum":["A","B","C","D","F"],"title":"Grade","description":"Letter grade derived from score: A=90+, B=75+, C=60+, D=40+, else F.","default":"F"},"findings":{"items":{"$ref":"#/components/schemas/HeaderFinding"},"type":"array","title":"Findings","description":"Per-header validation findings — one entry per header in the ruleset (present or missing)."},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary of grade + key gaps.","default":""},"headers_present":{"items":{"type":"string"},"type":"array","title":"Headers Present","description":"Names of security-relevant headers the origin actually sent."},"headers_missing":{"items":{"type":"string"},"type":"array","title":"Headers Missing","description":"Names of security-relevant headers the origin did NOT send."}},"type":"object","required":["domain"],"title":"ScanHeadersResponse"},"ScanResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Scanned domain (lowercased, no scheme/path/port)."},"resolved_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolved Ip","description":"IP the scanner pinned for the scan (SSRF defense — DNS resolved once, pre-validated; '127.0.0.1' for the self-domain bypass)."},"total_score":{"type":"integer","title":"Total Score","description":"Aggregate security score across all scanner modules.","default":0},"max_score":{"type":"integer","title":"Max Score","description":"Maximum achievable score for the modules that ran.","default":0},"grade":{"type":"string","title":"Grade","description":"Letter grade (A-F) derived from total_score/max_score.","default":""},"findings":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Findings","description":"Vulnerability findings sorted by severity (critical first). Each entry carries severity/category/title plus category-specific detail fields."},"findings_count":{"additionalProperties":{"type":"integer"},"type":"object","title":"Findings Count","description":"Finding counts keyed by severity: {critical, high, medium, low}."},"headers":{"additionalProperties":true,"type":"object","title":"Headers","description":"HTTP security-headers module block (score, max, details)."},"ssl":{"additionalProperties":true,"type":"object","title":"Ssl","description":"SSL/TLS module block (score, max, details)."},"dns":{"additionalProperties":true,"type":"object","title":"Dns","description":"DNS / email-security module block (score, max, details)."},"redirect":{"additionalProperties":true,"type":"object","title":"Redirect","description":"Redirect-chain module block (score, max, details)."},"disclosure":{"additionalProperties":true,"type":"object","title":"Disclosure","description":"Information-disclosure module block (score, max, details)."},"cookies":{"additionalProperties":true,"type":"object","title":"Cookies","description":"Cookie-flags module block (score, max, details)."},"dnssec":{"additionalProperties":true,"type":"object","title":"Dnssec","description":"DNSSEC module block (score, max, details)."},"methods":{"additionalProperties":true,"type":"object","title":"Methods","description":"HTTP-methods module block (score, max, details)."},"cors":{"additionalProperties":true,"type":"object","title":"Cors","description":"CORS-policy module block (score, max, details)."},"html":{"additionalProperties":true,"type":"object","title":"Html","description":"HTML-hygiene module block (score, max, details)."},"csp_analysis":{"additionalProperties":true,"type":"object","title":"Csp Analysis","description":"Deep CSP-analysis module block (score, max, details)."},"enterprise":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Enterprise","description":"Present only for known enterprise domains: {is_enterprise, company, note} scoring caveat (large-org infra legitimately omits some checks)."},"summary":{"type":"string","title":"Summary","description":"One-line scan summary (reserved — empty until a summarizer is wired).","default":""}},"type":"object","required":["domain"],"title":"ScanResponse","description":"Envelope for the ContrastScan engine result (scan/engine.py contrast_scan()).\n\nMirrors the engine dict 1:1. The eleven section blocks are dict-typed —\ntheir inner shape ({score, max, details, ...}) is owned by the C binary\n(scanner/src/contrastscan.c); findings-enrichment fields come from\nscan/findings.py. `verdict` + `next_calls` are inherited from\nBaseSuccessResponse."},"SearchHint":{"properties":{"tool":{"type":"string","const":"cve_lookup","title":"Tool","description":"Drill-down tool to call with any result ID from the list. Constrained to cve_lookup today; expand the Literal as new list endpoints get list-level hints."},"reason":{"type":"string","title":"Reason","description":"Short rationale explaining what the drill-down tool adds beyond the slim list items (e.g. full description, affected_products, references, exploit/KEV/CWE pivots)."}},"additionalProperties":true,"type":"object","required":["tool","reason"],"title":"SearchHint","description":"Footer hint emitted on list responses (cve_search, cve_leading) to point\nLLM agents at the natural drill-down tool. Distinct from PivotHint: there is\nno `input` field because the hint is global to the list — the agent picks a\nresult of interest and passes its ID to the named tool."},"SeoAuditResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed)."},"fetched_url":{"type":"string","title":"Fetched Url","description":"Final URL we fetched (post-redirects)."},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status returned by the homepage fetch."},"title_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title Untrusted","description":"`<title>` text, control-char stripped, capped at 300 chars. `_untrusted`."},"meta_description_untrusted":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meta Description Untrusted","description":"`<meta name='description'>` content, capped at 500 chars. `_untrusted`."},"canonical_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonical Url","description":"`<link rel='canonical'>` href, resolved to an absolute URL."},"h1_untrusted":{"items":{"type":"string"},"type":"array","title":"H1 Untrusted","description":"Text of each `<h1>` (capped at 20 entries, 300 chars each). `_untrusted`."},"h1_count":{"type":"integer","title":"H1 Count","description":"Total number of `<h1>` tags found (NOT capped — for scoring).","default":0},"h2_count":{"type":"integer","title":"H2 Count","description":"`<h2>` tag count, capped at 200.","default":0},"h3_count":{"type":"integer","title":"H3 Count","description":"`<h3>` tag count, capped at 200.","default":0},"images_total":{"type":"integer","title":"Images Total","description":"Total `<img>` tags on the page (parser bound: 1000).","default":0},"images_missing_alt":{"type":"integer","title":"Images Missing Alt","description":"Number of `<img>` tags with no `alt` attribute OR an empty/whitespace `alt`. Counts toward the score's accessibility rule.","default":0},"internal_link_count":{"type":"integer","title":"Internal Link Count","description":"`<a href>` count where the target host shares the registrable domain. Cheap eTLD-aware compare; not perfect on suffixes like .co.uk.","default":0},"external_link_count":{"type":"integer","title":"External Link Count","description":"`<a href>` count to a different registrable domain. Excludes mailto:, tel:, javascript:, in-page anchors.","default":0},"og_tags":{"additionalProperties":{"type":"string"},"type":"object","title":"Og Tags","description":"`<meta property='og:*'>` map, capped at 50 entries. Values capped at 500 chars each. All `_untrusted`."},"json_ld_present":{"type":"boolean","title":"Json Ld Present","description":"True if at least one `<script type='application/ld+json'>` block exists (parser does NOT validate the JSON, only counts tag presence — score considers tag presence sufficient for structured-data signal).","default":false},"score":{"type":"integer","title":"Score","description":"Composite 0-100 SEO score: 10 rules x 10 points each (title present, title length, meta description present, meta description length, single H1, canonical, >=3 OG tags, JSON-LD present, image alt coverage proportional, HTTPS)."},"missing_signals":{"items":{"type":"string"},"type":"array","title":"Missing Signals","description":"Rule-IDs that did NOT contribute their points. Subset of: title_missing, title_length_off, meta_description_missing, meta_description_length_off, h1_missing, h1_multiple, canonical_missing, og_tags_sparse, json_ld_missing, images_missing_alt, not_https."},"cache_respected":{"type":"boolean","title":"Cache Respected","description":"True if we wrote the result to our cache. False when the target sent `Cache-Control: no-store` or `private` and we honoured it.","default":true},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary.","default":""}},"type":"object","required":["domain","fetched_url","status_code","score"],"title":"SeoAuditResponse","description":"One-page SEO audit of a domain's homepage with a 0-100 composite score.\n\nStrictly homepage-only (path `/`); we do NOT crawl the site. Same\nethical floor as `brand_assets`: target's robots.txt is honoured\n(Disallow `/` for our UA → 403, no fetch). All target-derived\nstring/list fields are `_untrusted` (DO NOT execute or shell-out —\nthe page author controls these contents).\n\nScore (0-100) is the sum of 10 audit rules, each worth 0-10 points.\n`missing_signals` lists the rule-IDs that did NOT fire so agents\ncan surface concrete fixes (\"title_missing\", \"h1_multiple\", etc.)."},"ShodanRefItem":{"properties":{"id":{"type":"string","title":"Id","default":""},"description":{"type":"string","title":"Description","default":""},"source":{"type":"string","title":"Source","default":""}},"type":"object","title":"ShodanRefItem"},"ShodanRefSource":{"properties":{"found":{"type":"boolean","title":"Found","default":false},"count":{"type":"integer","title":"Count","default":0},"results":{"items":{"$ref":"#/components/schemas/ShodanRefItem"},"type":"array","title":"Results"},"truncated":{"type":"boolean","title":"Truncated","default":false},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"ShodanRefSource"},"ShodanRepInfo":{"properties":{"status":{"type":"string","enum":["ok","skipped","restricted","rate_limited","error","pro_only"],"title":"Status","description":"'ok' = data fetched; 'skipped' = API key not configured; 'restricted' = 403 (IP not available on free Shodan tier); 'rate_limited' = 429 quota exceeded; 'error' = transient HTTP/network failure; 'pro_only' = returned on Free tier as upsell hint."},"os":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Os","description":"Shodan-detected operating system (fingerprint-based, best-effort)."},"org":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org","description":"Organization name owning the IP per Shodan."},"isp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Isp","description":"ISP per Shodan (may differ from AbuseIPDB/RIPE)."},"asn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Asn","description":"ASN string per Shodan (e.g. 'AS13335'); may differ from top-level asn int."},"ports":{"items":{"type":"integer"},"type":"array","title":"Ports","description":"Open ports observed by Shodan full scan (superset of top-level InternetDB ports)."},"vulns":{"items":{"type":"string"},"type":"array","title":"Vulns","description":"CVE IDs Shodan has associated with banners on this IP."},"hostnames":{"items":{"type":"string"},"type":"array","title":"Hostnames","description":"Hostnames observed pointing to this IP per Shodan."},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City name per Shodan geolocation."},"country_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Name","description":"Country name per Shodan geolocation."},"last_update":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Update","description":"ISO 8601 timestamp of Shodan's most recent data point for this IP."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Human-readable reason. Present when status is skipped/restricted/rate_limited/error/pro_only."},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url","description":"Upgrade link returned when status='pro_only'."}},"type":"object","required":["status"],"title":"ShodanRepInfo","description":"Shodan full API enrichment (Pro tier only). Richer than InternetDB fields at top level."},"SigmaDetection":{"properties":{"selections":{"additionalProperties":true,"type":"object","title":"Selections","description":"Selection blocks keyed by name (e.g., {'selection_img': {...}, 'selection_cmd': {...}})"},"condition":{"type":"string","title":"Condition","description":"Boolean condition syntax (e.g., 'all of selection_*', 'selection_a and selection_b')","default":"unknown"}},"type":"object","title":"SigmaDetection","description":"Nested detection block — preserve raw YAML structure."},"SigmaRule":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"rule_id":{"type":"string","title":"Rule Id","description":"UUID of the rule (unique identifier)"},"title":{"type":"string","title":"Title","description":"Human-readable rule title"},"status":{"type":"string","enum":["test","stable","experimental","unsupported","deprecated"],"title":"Status","description":"Rule maturity level","default":"test"},"level":{"type":"string","enum":["informational","low","medium","high","critical"],"title":"Level","description":"Detection alert severity","default":"medium"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Multi-line rule description; may be None if omitted in YAML"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author","description":"Rule author(s); defaults to 'Unknown' if missing"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date","description":"ISO-8601 creation date"},"modified":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified","description":"ISO-8601 last modification date"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Flattened tags list (attack.t1059, cve.2024-1234, detection.threat_hunting, etc.)"},"logsource":{"additionalProperties":true,"type":"object","title":"Logsource","description":"Logsource metadata: {product, service, category, definition}"},"detection":{"$ref":"#/components/schemas/SigmaDetection","description":"Nested detection block with selections and condition"},"detection_summary":{"type":"string","title":"Detection Summary","description":"Human-readable summary (e.g., '2 selections, condition: all of selection_*')","default":""},"references":{"items":{"type":"string"},"type":"array","title":"References","description":"List of reference URLs from the rule"},"falsepositives":{"items":{"type":"string"},"type":"array","title":"Falsepositives","description":"Known false-positive scenarios"},"license":{"type":"string","title":"License","description":"Detection Rule License version","default":"DRL 1.1"},"source_url":{"type":"string","title":"Source Url","description":"GitHub URL to the rule in SigmaHQ repository (set by indexer)","default":""},"updated_at":{"type":"string","title":"Updated At","description":"ISO-8601 timestamp when ContrastAPI last synced this rule","default":""}},"type":"object","required":["rule_id","title","detection"],"title":"SigmaRule","description":"Full Sigma detection rule parsed from YAML."},"SigmaRuleLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"rule":{"$ref":"#/components/schemas/SigmaRule","description":"Full Sigma rule record"}},"additionalProperties":false,"type":"object","required":["rule"],"title":"SigmaRuleLookupResponse","description":"Single rule lookup response."},"SigmaRuleSearchResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up tool calls based on first result"},"rules":{"items":{"$ref":"#/components/schemas/SigmaRule"},"type":"array","title":"Rules","description":"Matching rules, capped by limit (default 50, max 200)"},"total_matches":{"type":"integer","title":"Total Matches","description":"Total candidates before limit/offset slicing","default":0},"limit":{"type":"integer","title":"Limit","description":"Effective limit applied to this response","default":50},"offset":{"type":"integer","title":"Offset","description":"Offset into the matched set","default":0},"truncated":{"type":"boolean","title":"Truncated","description":"True when total_matches > offset + limit (more pages available)","default":false}},"additionalProperties":false,"type":"object","title":"SigmaRuleSearchResponse","description":"Multi-rule search response (GET /v1/sigma/search)."},"SoaInfo":{"properties":{"mname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mname","description":"Primary nameserver (SOA MNAME)."},"rname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rname","description":"Responsible party mailbox (SOA RNAME)."},"serial":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Serial","description":"Zone serial number."}},"additionalProperties":true,"type":"object","title":"SoaInfo","description":"SOA record embedded inside DomainDnsInfo.soa."},"SpfMechanism":{"properties":{"type":{"type":"string","title":"Type","description":"Mechanism type"},"value":{"type":"string","title":"Value","description":"Mechanism value"},"qualifier":{"type":"string","enum":["+","-","~","?"],"title":"Qualifier","description":"Qualifier: + (pass), - (fail), ~ (softfail), ? (neutral)"}},"type":"object","required":["type","value","qualifier"],"title":"SpfMechanism","description":"Single SPF mechanism (a, mx, include, ip4, ip6, ptr, exists, redirect)."},"SpfPosture":{"properties":{"present":{"type":"boolean","title":"Present","description":"SPF record exists"},"record":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Record"},"all_policy":{"anyOf":[{"type":"string","enum":["permissive","soft_fail","strict","neutral"]},{"type":"null"}],"title":"All Policy"},"mechanisms":{"items":{"$ref":"#/components/schemas/SpfMechanism"},"type":"array","title":"Mechanisms"},"lookup_count":{"type":"integer","title":"Lookup Count","description":"DNS lookups needed"},"redirect_target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Target"},"has_spf_all":{"type":"boolean","title":"Has Spf All","description":"Has 'all' mechanism"},"findings":{"items":{"$ref":"#/components/schemas/Finding"},"type":"array","title":"Findings"}},"type":"object","required":["present","lookup_count","has_spf_all"],"title":"SpfPosture","description":"SPF posture analysis."},"SslChainItem":{"properties":{"subject":{"type":"string","title":"Subject","description":"Subject DN of the chain certificate, e.g. 'CN=*.example.com'.","default":""},"issuer":{"type":"string","title":"Issuer","description":"Issuer DN of the chain certificate (the CA that signed it).","default":""},"not_after":{"type":"string","title":"Not After","description":"Certificate's expiry timestamp (ISO 8601, UTC).","default":""},"source":{"type":"string","title":"Source","description":"How this chain entry was discovered: 'handshake' (server-sent) or 'aia_fetch' (AIA chase-up).","default":"handshake"}},"type":"object","title":"SslChainItem"},"SslInfoEmbedded":{"properties":{"common_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Common Name","description":"Leaf cert Subject CN."},"issuer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issuer","description":"Leaf cert issuer organization name."},"not_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Not Before","description":"notBefore timestamp (ISO 8601 / UTC)."},"not_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Not After","description":"notAfter (expiry) timestamp (ISO 8601 / UTC)."},"serial_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serial Number","description":"Hex-encoded cert serial number."},"version":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Version","description":"X.509 version as returned by the ssl module (int 3 for v3; empty string on some parse paths)."},"tls_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tls Version","description":"Negotiated TLS protocol (e.g. 'TLSv1.3', 'TLSv1.2'). Empty on handshake failure."},"alpn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpn","description":"Negotiated ALPN protocol (e.g. 'http/1.1', 'h2')."},"san":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"San","description":"Subject Alternative Names."},"days_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days Remaining","description":"Days until expiry. Negative when already expired."},"grade":{"anyOf":[{"type":"string","enum":["A","B","C","D","F"]},{"type":"null"}],"title":"Grade","description":"SSL grade. A/B/C: cert_valid AND TLS modern. D: cert readable but invalid (self-signed, hostname mismatch, untrusted root). F: probe failure, expired, or legacy TLS."},"cert_valid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Cert Valid","description":"True only when chain verified AND hostname matches AND not expired. False when cert is readable but fails one or more validation checks (see validation_errors)."},"validation_errors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Validation Errors","description":"Canonical validation failure tags when cert_valid is False. Values: 'expired', 'self_signed', 'hostname_mismatch', 'untrusted_root', 'chain_incomplete'. Empty/null when cert_valid is True."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Populated only on probe failure (timeout, connection refused, no port 443). Cert validation issues are NOT errors here — see cert_valid + validation_errors instead."}},"additionalProperties":true,"type":"object","title":"SslInfoEmbedded","description":"SSL subset embedded in the domain report. See top-level SslResponse for live SSL endpoint shape."},"SslResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (echoed). SNI-matched against the leaf cert."},"valid":{"type":"boolean","title":"Valid","description":"True when TLS handshake succeeded AND cert is unexpired AND chain verified. False on any failure (handshake error, expired, hostname mismatch, untrusted CA).","default":false},"issuer":{"type":"string","title":"Issuer","description":"Issuer DN of the leaf cert, e.g. \"CN=Let's Encrypt R3, O=Let's Encrypt, C=US\".","default":""},"subject":{"type":"string","title":"Subject","description":"Subject DN of the leaf cert, e.g. 'CN=example.com'.","default":""},"not_before":{"type":"string","title":"Not Before","description":"Leaf cert's notBefore timestamp (ISO 8601, UTC) — earliest valid moment.","default":""},"not_after":{"type":"string","title":"Not After","description":"Leaf cert's notAfter timestamp (ISO 8601, UTC) — expiry moment.","default":""},"days_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days Remaining","description":"Days until leaf cert expires (negative if already expired). Null when not_after could not be parsed."},"serial_number":{"type":"string","title":"Serial Number","description":"Hex-encoded leaf cert serial number.","default":""},"signature_algorithm":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature Algorithm","description":"Signature algorithm name, e.g. 'sha256WithRSAEncryption', 'ecdsa-with-SHA384'."},"san":{"items":{"type":"string"},"type":"array","title":"San","description":"Subject Alternative Names — all DNS names the cert is valid for (including CN when distinct)."},"protocol":{"type":"string","title":"Protocol","description":"Negotiated TLS protocol version string as reported by OpenSSL: 'TLSv1.3', 'TLSv1.2', 'TLSv1.1', 'TLSv1'. Empty on handshake failure. Grade F is forced for TLSv1/TLSv1.1.","default":""},"cipher":{"$ref":"#/components/schemas/CipherInfo","description":"Negotiated cipher suite with name, negotiated TLS protocol, and key length. All fields are null on handshake failure (empty CipherInfo)."},"chain":{"items":{"$ref":"#/components/schemas/SslChainItem"},"type":"array","title":"Chain","description":"Full cert chain from leaf upward (excluding system root). Includes AIA-fetched intermediates when needed."},"grade":{"type":"string","enum":["A","B","C","D","F"],"title":"Grade","description":"Overall SSL configuration grade. 'A' (cert_valid + TLSv1.3 + >=30 days remaining), 'B' (cert_valid + (TLSv1.3 <30d OR TLSv1.2 healthy)), 'C' (cert_valid + (TLSv1.2 <14d OR TLSv1.3 <7d OR unknown protocol)), 'D' (cert readable but invalid: hostname_mismatch / untrusted_root / self_signed), 'F' (probe failure, expired, OR TLSv1/TLSv1.1). Canonical grader is _ssl_grade() in domain/recon.py; same helper powers /v1/domain/ ssl section (single source of truth).","default":"F"},"validation_errors":{"items":{"type":"string"},"type":"array","maxItems":10,"title":"Validation Errors","description":"Canonical cert validation failure tags when cert is readable but invalid. Values: 'expired', 'self_signed', 'hostname_mismatch', 'untrusted_root', 'chain_incomplete'. Empty when cert validates cleanly. See also: 'valid' (boolean overall) and 'warnings' (human-readable)."},"warnings":{"items":{"type":"string"},"type":"array","maxItems":10,"title":"Warnings","description":"Human-readable warnings: deprecated protocol, near-expiry, self-signed chain, weak signature algorithm, etc."},"summary":{"type":"string","title":"Summary","description":"One-line human summary, e.g. 'example.com valid until 2026-07-04 (71 days) · TLSv1.3 · grade A'.","default":""}},"type":"object","required":["domain"],"title":"SslResponse"},"SubdomainsInfo":{"properties":{"subdomains":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Subdomains","description":"Sorted unique subdomain list."},"count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Count","description":"Total subdomains discovered."},"sources":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Sources","description":"Sources that produced hits (subset of ['wordlist', 'crt_sh'])."},"found_via_wordlist":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Found Via Wordlist","description":"Count discovered via DNS brute-force wordlist."},"found_via_crtsh":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Found Via Crtsh","description":"Count discovered via crt.sh CT log query."},"wildcard_status":{"anyOf":[{"type":"string","enum":["absent","present","undetermined"]},{"type":"null"}],"title":"Wildcard Status","description":"Wildcard-DNS probe result; anything other than 'absent' means count is unverified."},"warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Warnings","description":"Non-fatal warnings (e.g. 'crt.sh timeout', 'result truncated')."},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary","description":"One-line human-readable summary."}},"additionalProperties":true,"type":"object","title":"SubdomainsInfo"},"SubdomainsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"count":{"type":"integer","title":"Count","default":0},"subdomains":{"items":{"type":"string"},"type":"array","title":"Subdomains"},"summary":{"type":"string","title":"Summary","default":""},"sources":{"items":{"type":"string"},"type":"array","title":"Sources"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"},"found_via_wordlist":{"type":"integer","title":"Found Via Wordlist","default":0},"found_via_crtsh":{"type":"integer","title":"Found Via Crtsh","default":0},"wildcard_status":{"type":"string","enum":["absent","present","undetermined"],"title":"Wildcard Status","description":"Result of two synthetic negative-control DNS probes. 'absent' = no catch-all, the wordlist plane is trustworthy. 'present' = wildcard DNS (*.domain) answers every name, so brute-force cannot distinguish a real host from the catch-all; wordlist results are discarded (found_via_wordlist=0) and count is a certificate-transparency LOWER BOUND — the real surface is UNKNOWN, not small. 'undetermined' = a probe went unanswered or the target name was too long to probe, so the count is unverified and may contain artefacts. Treat anything other than 'absent' as a measurement caveat.","default":"absent"},"crtsh_status":{"type":"string","enum":["ok","timeout","rate_limited","unavailable","error"],"title":"Crtsh Status","description":"Status of the crt.sh certificate-transparency lookup that feeds found_via_crtsh. 'ok' means the upstream responded — found_via_crtsh=0 with status='ok' is a real empty result. Anything else means the upstream did not deliver (timeout / rate_limited / unavailable / error); count and subdomains are then wordlist-only and an unknown number of CT-log subdomains may be missing.","default":"ok"}},"type":"object","required":["domain"],"title":"SubdomainsResponse"},"TechItem":{"properties":{"name":{"type":"string","title":"Name"},"category":{"type":"string","title":"Category"},"source":{"type":"string","title":"Source"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"}},"type":"object","required":["name","category","source"],"title":"TechItem"},"TechResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"technologies":{"items":{"$ref":"#/components/schemas/TechItem"},"type":"array","title":"Technologies"},"categories":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Categories"},"count":{"type":"integer","title":"Count","default":0},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain"],"title":"TechResponse"},"TechVulnItem":{"properties":{"technology":{"type":"string","title":"Technology"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"cve_count":{"type":"integer","title":"Cve Count","default":0},"cves":{"items":{"$ref":"#/components/schemas/CveVulnItem"},"type":"array","title":"Cves"}},"type":"object","required":["technology"],"title":"TechVulnItem"},"ThreatFoxSource":{"properties":{"found":{"type":"boolean","title":"Found","description":"True when ThreatFox returned at least one IOC entry for the indicator."},"malware":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Malware","description":"Malware family name (e.g. 'Cobalt Strike'). Null when found=False."},"threat_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Threat Type","description":"Threat classification (e.g. 'botnet_cc', 'payload_delivery'). Null when found=False."},"confidence":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Confidence","description":"ThreatFox confidence score (0-100). Null when found=False or not provided upstream."},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"ThreatFox tags. May include 'test'/'demo' for honeypot entries."},"first_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen","description":"ISO timestamp of first ThreatFox observation. Null when found=False."},"ioc_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ioc Count","description":"Total ThreatFox IOC entries matching this indicator. Null when found=False."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"'upstream timeout' or 'upstream error' when ThreatFox query failed; absent on success."}},"type":"object","required":["found"],"title":"ThreatFoxSource","description":"ThreatFox abuse.ch source entry inside IocResponse.sources.threatfox."},"ThreatInfo":{"properties":{"urlhaus_status":{"anyOf":[{"type":"string","enum":["clean","listed","error","skipped"]},{"type":"null"}],"title":"Urlhaus Status","description":"URLhaus lookup outcome. 'skipped' in lite mode; 'error' on API failure (treat as unavailable, not clean)."},"url_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Count","description":"Total URLs URLhaus has seen for this domain."},"urls_online":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Urls Online","description":"Subset of url_count currently marked online."},"threat_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Threat Types","description":"Deduped list of threat classes across all URLs."},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags","description":"Deduped list of tags (up to 20)."},"urls":{"anyOf":[{"items":{"$ref":"#/components/schemas/ThreatUrlEntry"},"type":"array"},{"type":"null"}],"title":"Urls","description":"Up to 20 offending URL entries."}},"additionalProperties":true,"type":"object","title":"ThreatInfo"},"ThreatReportResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"ip":{"type":"string","title":"Ip","description":"Queried IP address (IPv4 or IPv6, echoed back verbatim)."},"enrichment":{"$ref":"#/components/schemas/IpEnrichmentInfo","description":"Shodan InternetDB free-tier enrichment (ports, hostnames, vulns, cpes, tags). Available on all tiers. See IpEnrichmentInfo for the exact field shape. Returned with all-empty lists on upstream failure — treat as 'no data', not 'clean'."},"abuseipdb":{"$ref":"#/components/schemas/AbuseIpdbInfo","description":"AbuseIPDB abuse-confidence enrichment. Pro tier returns live data; Free tier returns a {status:'pro_only', reason, upgrade_url} upsell stub (NOT an error). Pro failure paths emit status='error' / 'rate_limited' / 'skipped'. See AbuseIpdbInfo."},"shodan":{"$ref":"#/components/schemas/ShodanRepInfo","description":"Shodan full-API enrichment (richer than the InternetDB enrichment block). Pro tier returns live data; Free tier returns a {status:'pro_only', reason, upgrade_url} upsell stub. Pro failure paths emit status='error' / 'rate_limited' / 'restricted' / 'skipped'. See ShodanRepInfo."},"asn":{"additionalProperties":true,"type":"object","title":"Asn","description":"ASN ownership from RIPE Stat network-info: {asn: int, prefix: str}. Empty dict when RIPE has no allocation; {error:'lookup_failed'} on fetch failure."},"threat_level":{"type":"string","enum":["none","low","medium","high"],"title":"Threat Level","description":"Heuristic threat tier. 'high' when any vulns present OR abuse_score>=50; 'medium' when abuse_score>=25; 'low' when open ports observed; 'none' otherwise. On Free tier threat_level is necessarily conservative — abuse_score is unknown.","default":"none"},"summary":{"type":"string","title":"Summary","description":"One-line human summary combining threat_level, port count, vuln count, and abuse signal.","default":""},"ptr":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ptr","description":"Reverse DNS PTR for the IP, or null when unresolvable."},"asn_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Asn Name","description":"ASN holder name from RIPE Stat as-overview, or null."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"Country code from RIPE Stat rir-stats-country, or null."},"cloud_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cloud Provider","description":"Cloud / hosting provider name when the IP sits in a known CIDR or maps to a tier-1 ASN."},"is_datacenter":{"type":"boolean","title":"Is Datacenter","description":"True if IP is hosted on a known datacenter / cloud provider (parity with ip_lookup.is_datacenter). Same two-tier detection — cloud_provider hit OR tier-1 datacenter ASN. Always present — never null.","default":false},"tor_exit":{"type":"boolean","title":"Tor Exit","description":"True if IP appears in the Tor Project bulk exit list (verdict.sources_unavailable['tor'] when fetch failed).","default":false},"firehol":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Firehol","description":"FireHOL Level1 listing status: {status, listed, lists_matched}. Available on all tiers."},"risk_score":{"type":"integer","title":"Risk Score","description":"Composite 0-100 score (parity with ip_lookup.risk_score). v1.17.0 additive components: ports (10 * min(count, 5) = 0-50), tor_exit (+30), firehol.listed (+20), AbuseIPDB confidence (round(15 * score / 100) = 0-15), is_datacenter (+10), known vulns (5 * min(count, 4) = 0-20). Use severity_label for thresholding.","default":0},"severity_label":{"type":"string","enum":["low","medium","high","critical"],"title":"Severity Label","description":"Coarse risk band derived from risk_score (parity with ip_lookup.severity_label). Pre-1.17 the route emitted this field and advertised it in verdict.falsifiable_fields but the schema didn't declare it, so Pydantic silently dropped it from the wire. Same thresholds: >=75 critical, >=50 high, >=25 medium, else low.","default":"low"}},"type":"object","required":["ip"],"title":"ThreatReportResponse"},"ThreatResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"urlhaus_status":{"type":"string","title":"Urlhaus Status"},"urls_online":{"type":"integer","title":"Urls Online","default":0},"url_count":{"type":"integer","title":"Url Count","default":0},"threat_types":{"items":{"type":"string"},"type":"array","title":"Threat Types"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"urls":{"items":{"$ref":"#/components/schemas/ThreatUrl"},"type":"array","title":"Urls"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain","urlhaus_status"],"title":"ThreatResponse"},"ThreatUrl":{"properties":{"url":{"type":"string","title":"Url","default":""},"status":{"type":"string","title":"Status","default":"unknown"},"threat":{"type":"string","title":"Threat","default":"unknown"},"date_added":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Added"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"}},"type":"object","title":"ThreatUrl"},"ThreatUrlEntry":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Offending URL observed in URLhaus."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"URLhaus status for this URL ('online', 'offline')."},"threat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Threat","description":"Threat class (e.g. 'malware_download', 'phishing')."},"date_added":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Added","description":"When URLhaus first saw this URL."},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags","description":"Tags assigned by URLhaus (malware family, kit, etc.)."}},"additionalProperties":true,"type":"object","title":"ThreatUrlEntry","description":"Single offending URL entry inside ThreatInfo.urls."},"TorSource":{"properties":{"listed":{"type":"boolean","title":"Listed","description":"True when the IP appears in the Tor Project's bulk exit list."},"fetch_status":{"type":"string","enum":["initial","ok","failed","capped"],"title":"Fetch Status","description":"Cache state of the Tor exit list snapshot used for the lookup. 'initial' = no refresh has run yet; 'ok' = fresh fetch; 'failed' = upstream fetch failed (treat listed=False as 'unknown', not 'safe'); 'capped' = upstream response exceeded the size cap and was rejected."}},"type":"object","required":["listed","fetch_status"],"title":"TorSource","description":"Tor exit list entry inside IocResponse.sources.tor (IP only)."},"UrlhausHostDetail":{"properties":{"found":{"type":"boolean","title":"Found","default":false},"urls_online":{"type":"integer","title":"Urls Online","default":0},"url_count":{"type":"integer","title":"Url Count","default":0}},"type":"object","title":"UrlhausHostDetail"},"UrlhausSource":{"properties":{"found":{"type":"boolean","title":"Found","description":"True when URLhaus has at least one URL for the indicator."},"urls_online":{"type":"integer","title":"Urls Online","description":"Subset of URLhaus URLs currently marked online.","default":0}},"type":"object","required":["found"],"title":"UrlhausSource","description":"URLhaus abuse.ch source entry inside IocResponse.sources.urlhaus."},"UrlhausUrlDetail":{"properties":{"found":{"type":"boolean","title":"Found","default":false},"threat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Threat"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"URLhaus url_status for the exact URL match: 'online' (active threat), 'offline' (historical, threat may be cleaned up), or 'unknown'. Null when the URL was not found."}},"type":"object","title":"UrlhausUrlDetail"},"UsernameLookupResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"username":{"type":"string","title":"Username","description":"Echoed normalized username (lowercased, validated against [a-z0-9._-]).","default":""},"found_count":{"type":"integer","title":"Found Count","description":"Number of platforms where status=='found'.","default":0},"checked_count":{"type":"integer","title":"Checked Count","description":"Number of platforms actually checked (may be less than total platforms if early-exit).","default":0},"results":{"items":{"$ref":"#/components/schemas/UsernameMatch"},"type":"array","title":"Results","description":"Per-platform results, sorted: found first, then alphabetical by platform."},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary, e.g. 'username \"x\" found on 6/20 platforms (3 unavailable)'.","default":""},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Input validation error (empty username, invalid chars, too long). Null on successful lookups."}},"type":"object","title":"UsernameLookupResponse"},"UsernameMatch":{"properties":{"platform":{"type":"string","title":"Platform","description":"Platform identifier, e.g. 'github', 'twitter', 'reddit'.","default":""},"url":{"type":"string","title":"Url","description":"Canonical profile URL for this platform+username (may 200/redirect even when not_found).","default":""},"status":{"type":"string","enum":["found","not_found","rate_limited","blocked","timeout","error"],"title":"Status","description":"Per-platform outcome. 'found'/'not_found' are terminal factual answers. 'rate_limited' (429), 'blocked' (403 — often Cloudflare/bot detection), 'timeout' (network), and 'error' (5xx/other) are unavailability states — the platform's answer is unknown, NOT 'user does not exist'. Agents should treat these four as sources_unavailable, not negative evidence.","default":"error"}},"type":"object","title":"UsernameMatch"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"Verdict":{"properties":{"deterministic":{"type":"boolean","title":"Deterministic","description":"True when the response is fully reproducible from the listed sources for the same input at the same moment (no randomness, no model inference). False for endpoints that include probabilistic scoring or LLM output."},"falsifiable_fields":{"items":{"type":"string"},"type":"array","title":"Falsifiable Fields","description":"Top-level response fields whose values a caller can independently re-derive from the named upstream sources (e.g. 'dns', 'ssl', 'whois'). Fields not in this list are derived/computed and cannot be directly re-verified."},"data_age_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Data Age Seconds","description":"Seconds elapsed since the oldest cached source was fetched, or null when every source was queried live for this request. Use to judge freshness."},"sources_queried":{"items":{"type":"string"},"type":"array","title":"Sources Queried","description":"Canonical source identifiers successfully consulted for this response (e.g. 'ripe_stat', 'shodan_internetdb', 'firehol'). Agent-readable list, order not significant."},"sources_unavailable":{"items":{"type":"string"},"type":"array","title":"Sources Unavailable","description":"Sources that were expected but not returned — either intentionally skipped (lite mode, tier gating) or failed (quota, timeout, upstream down). Empty list means every planned source produced data."},"completeness":{"type":"string","enum":["complete","partial","minimal"],"title":"Completeness","description":"'complete' = every planned source returned data; 'partial' = at least one source in sources_unavailable failed or was skipped; 'minimal' = only the primary/required source returned, optional enrichment missing.","default":"complete"}},"type":"object","required":["deterministic"],"title":"Verdict"},"VulnInfo":{"properties":{"cve_id":{"type":"string","title":"Cve Id","description":"CVE identifier (e.g. 'CVE-2021-44228')."},"severity":{"type":"string","enum":["CRITICAL","HIGH","MEDIUM","LOW","UNKNOWN"],"title":"Severity","description":"NVD CVSS v3 severity bucket from local cve.db. 'UNKNOWN' when the CVE is not in our database (NVD may not have classified it yet, or the ID is reserved). Treat UNKNOWN as 'do not assume benign — call cve_lookup for fresh upstream data.'"},"cvss_v3":{"anyOf":[{"type":"number","maximum":10.0,"minimum":0.0},{"type":"null"}],"title":"Cvss V3","description":"CVSS v3 base score (0.0-10.0). Null when severity='UNKNOWN' or NVD has no v3 score."}},"type":"object","required":["cve_id","severity"],"title":"VulnInfo","description":"Severity-enriched CVE entry attached to /v1/ip and /v1/threat_report.\n\nPhase 2 IP enrichment (v1.16.0 BREAKING): Shodan InternetDB returns a flat\nlist of CVE IDs with no severity context, forcing agents to fan out\ncve_lookup calls for triage. We resolve severity + cvss_v3 against the\nlocal cve.db in a single SQL batch so the agent can prioritise without\nextra round-trips. Unknown CVEs are emitted with severity='UNKNOWN' /\ncvss_v3=null so the ID is preserved (the agent must not infer 'benign'\nfrom the absence of a row)."},"VulnsResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"technologies_scanned":{"type":"integer","title":"Technologies Scanned","default":0},"total_cves":{"type":"integer","title":"Total Cves","default":0},"vulnerabilities":{"items":{"$ref":"#/components/schemas/TechVulnItem"},"type":"array","title":"Vulnerabilities"},"summary":{"type":"string","title":"Summary","default":""}},"type":"object","required":["domain"],"title":"VulnsResponse"},"WafInfo":{"properties":{"detected":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Detected","description":"WAF product names detected from response headers (e.g. ['Cloudflare', 'AWS CloudFront'])."},"waf_present":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Waf Present","description":"True when `detected` is non-empty."}},"additionalProperties":true,"type":"object","title":"WafInfo"},"WaybackResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain"},"status":{"type":"string","enum":["ok","unavailable"],"title":"Status","description":"'ok' when the CDX request returned a parseable response (even if zero snapshots); 'unavailable' when CDX timed out, rate-limited, 5xx-failed, or returned malformed data. On 'unavailable' total_snapshots is omitted (unknown) — DO NOT interpret absence as zero. See warnings[] for the specific cdx_* error code.","default":"ok"},"total_snapshots":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Snapshots","description":"Snapshot count when status='ok'. Omitted (null) when status='unavailable' — the count is unknown, NOT zero. Use the archive_url to check manually in that case."},"first_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen"},"last_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Seen"},"years_online":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Years Online","description":"Years between first_seen and last_seen. Omitted when status='unavailable'."},"snapshots":{"items":{"$ref":"#/components/schemas/WaybackSnapshot"},"type":"array","title":"Snapshots"},"archive_url":{"type":"string","title":"Archive Url","default":""},"summary":{"type":"string","title":"Summary","default":""},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["domain"],"title":"WaybackResponse"},"WaybackSnapshot":{"properties":{"timestamp":{"type":"string","title":"Timestamp"},"date":{"type":"string","title":"Date"},"status":{"type":"string","title":"Status"},"mimetype":{"type":"string","title":"Mimetype"},"url":{"type":"string","title":"Url"}},"type":"object","required":["timestamp","date","status","mimetype","url"],"title":"WaybackSnapshot"},"WhoisInfoEmbedded":{"properties":{"registrar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Registrar","description":"Registrar name as reported by the WHOIS server."},"creation_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creation Date","description":"Domain creation date (format depends on registrar)."},"expiry_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiry Date","description":"Domain expiry date (format depends on registrar)."},"updated_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated Date","description":"Last-updated timestamp from WHOIS."},"name_servers":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Name Servers","description":"Authoritative nameservers per WHOIS."},"status":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Status","description":"EPP domain status (e.g. 'clientTransferProhibited'). String or list depending on registrar."},"raw_length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Raw Length","description":"Byte length of raw WHOIS response (sanity indicator)."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Populated when the WHOIS TCP query failed (e.g. no WHOIS server for TLD, socket timeout)."}},"additionalProperties":true,"type":"object","title":"WhoisInfoEmbedded","description":"WHOIS subset embedded in the domain report. Fields are best-effort regex extracts from the raw WHOIS text."},"WhoisResponse":{"properties":{"verdict":{"anyOf":[{"$ref":"#/components/schemas/Verdict"},{"type":"null"}],"description":"Falsifiability metadata: sources_queried, sources_unavailable, completeness, deterministic flag. Lets agents distinguish 'no data' from 'source failed' without re-running the call."},"next_calls":{"anyOf":[{"items":{"$ref":"#/components/schemas/PivotHint"},"type":"array"},{"type":"null"}],"title":"Next Calls","description":"Suggested follow-up MCP tool calls. Ordered by relevance; agents should chain these without re-prompting the user."},"domain":{"type":"string","title":"Domain","description":"Queried domain (lowercased, no scheme)."},"whois":{"$ref":"#/components/schemas/WhoisInfoEmbedded","description":"WHOIS extract — registrar, dates, nameservers, EPP status. Same shape as DomainReportResponse.whois. Populates `error` when the WHOIS query failed (no WHOIS server for TLD, socket timeout, etc.)."},"summary":{"type":"string","title":"Summary","description":"One-line human-readable summary (registrar + expiry hint).","default":""}},"type":"object","required":["domain","whois"],"title":"WhoisResponse"},"_BulkAtlasTechniqueRequest":{"properties":{"technique_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Technique Ids","description":"List of ATLAS technique ids in canonical form 'AML.T####' or 'AML.T####.###' (case-insensitive; normalized to upper-case + de-duplicated server-side). Truncated to 50 entries before lookup."}},"type":"object","title":"_BulkAtlasTechniqueRequest"},"_BulkCveRequest":{"properties":{"cve_ids":{"items":{"type":"string","maxLength":64},"type":"array","maxItems":50,"title":"Cve Ids","description":"List of CVE identifiers in canonical form 'CVE-YYYY-NNNN+' (case-insensitive; normalized to upper-case + de-duplicated server-side). Each CVE counts as 1 request toward the rate limit; ids beyond the caller's remaining hourly quota land in `skipped_due_to_rate_limit`. Max 50 ids per call (Pydantic input cap). Empty list returns 200 + empty results (parity with bulk_atlas + bulk_ioc)."},"include_affected_products":{"type":"boolean","title":"Include Affected Products","description":"Return full affected_products list for each CVE (default: first 20).","default":false},"include_full_references":{"type":"boolean","title":"Include Full References","description":"Return full references list for each CVE (default: first 10). total_references is always emitted.","default":false},"include_reference_tags":{"type":"boolean","title":"Include Reference Tags","description":"Return structured references_full per CVE in batch [{url, tags, source}]. Same shape as cve_lookup. Default False (backward compat).","default":false},"include_severity_breakdown":{"type":"boolean","title":"Include Severity Breakdown","description":"Return severity_sources/consensus/disagreement per CVE in batch. Same shape as cve_lookup. Default False (backward compat). cvss_v2 / cvss_v2_vector are always emitted (additive non-opt-in).","default":false}},"type":"object","title":"_BulkCveRequest"},"_BulkIocRequest":{"properties":{"indicators":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Indicators","description":"List of indicators of compromise — each is auto-detected per-item (IP / domain / URL / file hash MD5/SHA1/SHA256/SHA512). Mixed types in one batch are supported. Each indicator consumes 1 unit of the per-hour quota; entries beyond the caller's remaining quota land in `skipped_due_to_rate_limit`. Max 50 per call (Pydantic input cap)."}},"type":"object","title":"_BulkIocRequest"},"_BulkRequest":{"properties":{"domains":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Domains","description":"List of registrable domain names to report on (e.g. ['example.com', 'api.example.com']). No scheme, no path, no port. Punycode/IDN accepted; subdomains allowed. Each domain consumes 1 unit of the per-hour quota; entries beyond the caller's remaining quota land in `skipped_due_to_rate_limit` (v1.27 dynamic budget)."}},"type":"object","required":["domains"],"title":"_BulkRequest"}},"securitySchemes":{"ContrastAPIKey":{"type":"http","description":"Optional. Pass `Authorization: Bearer cc_<48 hex>` (or `X-API-Key: cc_<48 hex>`) for Pro tier (500/hr). Omit for keyless Free tier (30/hr/IP). Get a key at https://api.contrastcyber.com/pricing.","scheme":"bearer"}}}}