{"openapi":"3.1.0","info":{"title":"StalkPhish Scanner API","description":"\nScan any URL for phishing, brand impersonation and Phishing-as-a-Service kits.\n\n**Asynchronous by design.** A scan is a job:\n\n1. `POST /api/scan` with a URL → **202** `{ \"uuid\", \"status\": \"queued\" }`.\n2. Poll `GET /api/scan/{uuid}` until `status` is `done` (or `error`).\n3. When done, `result` holds the score, verdict and a bounded forensic `report`.\n\nThe edge only *enqueues* — it never fetches the target itself (an isolated worker does,\nthrough proxies). Defanged URLs are accepted (`hxxps://evil[.]com` → `https://evil.com`).\nSubmissions are rate-limited per IP and may be shed with **503** under heavy load.\n\nResults are informational only, produced by automated tests, without warranty.\n","version":"1.0"},"paths":{"/api/scan":{"post":{"tags":["Scan"],"summary":"Submit a URL for scanning","description":"Enqueue a scan; returns its UUID immediately (async job). Order: capacity\nbackpressure → access (per-IP quota, or beta access-key + per-key quota) →\nhCaptcha (open mode only) → enqueue.","operationId":"submit_scan_api_scan_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanRequest"}}},"required":true},"responses":{"202":{"description":"Queued — poll GET /api/scan/{uuid}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanAccepted"}}}},"401":{"description":"Missing/invalid access code (beta_keys mode)"},"422":{"description":"Not a valid http(s) URL"},"429":{"description":"Rate limit / quota exceeded"},"503":{"description":"Scanner busy — retry after a moment"}}}},"/api/scan/{uuid}":{"get":{"tags":["Scan"],"summary":"Get scan status / result","description":"Poll a scan by UUID. 404 if unknown or expired (TTL).","operationId":"get_scan_api_scan__uuid__get","parameters":[{"name":"uuid","in":"path","required":true,"schema":{"type":"string","title":"Uuid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanStatusResponse"}}}},"404":{"description":"Unknown or expired scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/scan/{uuid}/screenshot":{"get":{"tags":["Scan"],"summary":"Get the rendered screenshot (PNG)","description":"Serve the rendered PNG for a scan, same-origin (never hotlinked).","operationId":"get_screenshot_api_scan__uuid__screenshot_get","parameters":[{"name":"uuid","in":"path","required":true,"schema":{"type":"string","title":"Uuid"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"No screenshot for this scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/report":{"post":{"tags":["Feedback"],"summary":"Report a false positive or issue","description":"Beta feedback: report a false positive / issue on a specific scan. The UUID\nties the report to exactly what was analysed; verdict/URL are attached\nserver-side (never trusted from the client).","operationId":"submit_report_api_report_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportAccepted"}}}},"404":{"description":"Unknown or expired scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Scan Page","description":"Landing + scan form (indexable). In beta_keys mode the page stays public (SEO\nintact) but the scan ACTION is gated: an access-code overlay replaces the form\nuntil a valid code is held. Magic link `/?code=bk-…` validates, sets the cookie\nand redirects to a clean URL.","operationId":"scan_page__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/results/{uuid}":{"get":{"summary":"Results Page","description":"Result page (noindex). JS polls GET /api/scan/{uuid} and renders.","operationId":"results_page_results__uuid__get","parameters":[{"name":"uuid","in":"path","required":true,"schema":{"type":"string","title":"Uuid"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ReportAccepted":{"properties":{"ref":{"type":"string","title":"Ref"},"status":{"type":"string","title":"Status","default":"received"}},"type":"object","required":["ref"],"title":"ReportAccepted","description":"202 response to a feedback report — `ref` is a short reference id."},"ReportRequest":{"properties":{"uuid":{"type":"string","maxLength":64,"title":"Uuid"},"type":{"type":"string","title":"Type","description":"false_positive|false_negative|wrong_details|bug"},"comment":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Comment"},"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}],"title":"Email"},"website":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Website"}},"type":"object","required":["uuid","type"],"title":"ReportRequest","description":"A beta feedback report tied to a scan UUID. Verdict/URL are attached\nserver-side from the stored scan — never trusted from the client. `website` is a\nhoneypot: humans never see it, so a filled value marks a bot."},"ScanAccepted":{"properties":{"uuid":{"type":"string","title":"Uuid"},"status":{"type":"string","title":"Status","default":"queued"}},"type":"object","required":["uuid"],"title":"ScanAccepted","description":"202 response to a submission — poll GET /api/scan/{uuid} for the outcome."},"ScanRequest":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":3,"title":"Url","description":"URL to scan (scheme optional; https assumed)"},"captcha_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Captcha Token","description":"hCaptcha response token (required when enforced)"}},"type":"object","required":["url"],"title":"ScanRequest","description":"A scan submission. `url` is refanged (hxxps://x[.]com → https://x.com),\nnormalized (bare host → https://) and validated to be an http(s) URL with a\nhost; the engine re-normalizes defensively too."},"ScanStatusResponse":{"properties":{"uuid":{"type":"string","title":"Uuid"},"status":{"type":"string","title":"Status"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"progress":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Progress"},"queued_at":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Queued At"},"started_at":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Finished At"}},"type":"object","required":["uuid","status"],"title":"ScanStatusResponse","description":"Current state of a scan. `result` is populated once status == \"done\"."},"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"}}},"tags":[{"name":"Scan","description":"Submit a URL and retrieve its status / result / screenshot."},{"name":"Feedback","description":"Report a false positive or an issue on a scan."}]}