{"openapi":"3.1.0","info":{"title":"ZygenTrust API","version":"0.4.0","description":"Domain and website trust intelligence API. Lightweight pre-screening with explainable signals.","contact":{"name":"ZygenTrust","url":"https://zygentrust.com"}},"servers":[{"url":"https://zygentrust-api.salarylab.workers.dev","description":"Production API"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"AnalyzeResponse":{"type":"object","required":["domain","normalized_domain","trust_score","risk_level","recommended_action","confidence","summary","core_trust_score","business_posture","known_threat_status","threat_sources_checked","threat_enrichment","coverage_level","decision_confidence","identity_verification","evidence_coverage","evidence_freshness","confidence_reasons","signals","insights","next_steps","metadata"],"properties":{"domain":{"type":"string"},"normalized_domain":{"type":"string"},"trust_score":{"type":"integer","minimum":0,"maximum":100},"risk_level":{"type":"string","enum":["low","medium","high"]},"recommended_action":{"type":"string","enum":["approve","manual_review","reject_or_verify"]},"confidence":{"type":"number","minimum":0,"maximum":1},"summary":{"type":"string"},"core_trust_score":{"type":"integer","minimum":0,"maximum":100},"business_posture":{"type":"string","enum":["strong","moderate","weak"]},"known_threat_status":{"type":"string","enum":["known_threat_hit","no_known_hit","unknown"]},"threat_sources_checked":{"type":"array","items":{"type":"string"}},"threat_source_states":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"status":{"type":"string","enum":["available_no_hit","available_hit","unavailable","not_checked","not_entitled"]}},"required":["source","status"]}},"threat_enrichment":{"type":"string","enum":["not_included","not_checked","checked_no_known_hit","checked_hit","check_failed"]},"coverage_level":{"type":"string","enum":["core","enhanced","deep"]},"decision_confidence":{"type":"string","enum":["standard","higher","deep"]},"decision_policy":{"type":"object","properties":{"outcome":{"type":"string","enum":["approve","approve_with_monitoring","manual_review","verify_first","reject"]},"rationale":{"type":"array","items":{"type":"string"}},"confidence":{"type":"number","minimum":0,"maximum":1},"hardEvidencePresent":{"type":"boolean"},"conflictingEvidencePresent":{"type":"boolean"},"ruleVersion":{"type":"string"}},"required":["outcome","rationale","confidence","hardEvidencePresent","conflictingEvidencePresent","ruleVersion"]},"risk_classification":{"type":"object","properties":{"site_intent":{"type":"object","properties":{"label":{"type":"string","enum":["operating_business","gambling","unknown"]},"confidence":{"type":"string","enum":["low","medium","high"]},"reasons":{"type":"array","items":{"type":"string"}},"visibility":{"type":"string","enum":["preview","full"]}},"required":["label","confidence","reasons","visibility"]},"abuse_risk":{"$ref":"#/components/schemas/RiskAssessment"},"scam_risk":{"$ref":"#/components/schemas/RiskAssessment"},"phishing_risk":{"$ref":"#/components/schemas/RiskAssessment"},"impersonation_risk":{"$ref":"#/components/schemas/RiskAssessment"},"payment_abuse_risk":{"$ref":"#/components/schemas/RiskAssessment"},"financial_obfuscation_risk":{"$ref":"#/components/schemas/RiskAssessment"}}},"locked_risk_modules":{"type":"array","items":{"type":"string","enum":["site_intent","abuse_risk","scam_risk","phishing_risk","impersonation_risk","payment_abuse_risk","financial_obfuscation_risk"]}},"decision_context":{"type":"object","properties":{"headline":{"type":"string"},"summary":{"type":"string"},"review_note":{"type":"string"},"primary_risk_focus":{"type":"string","enum":["core_posture","site_intent","abuse_risk","scam_risk","phishing_risk","impersonation_risk","payment_abuse_risk","financial_obfuscation_risk"]},"highlighted_reasons":{"type":"array","items":{"type":"string"}},"plan_note":{"type":"string"}},"required":["headline","summary","review_note","primary_risk_focus","highlighted_reasons"]},"identity_verification":{"type":"string","enum":["not_performed"]},"evidence_coverage":{"type":"string","enum":["low","medium","high"]},"evidence_freshness":{"type":"string","enum":["live","cached","stale"]},"evidence_log":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"observedAt":{"type":"string","format":"date-time"},"freshness":{"type":"string","enum":["live","cached","historical","unknown"]},"confidence":{"type":"number","minimum":0,"maximum":1},"locator":{"type":"string"},"summary":{"type":"string"},"rawValue":{"type":"string"}},"required":["source","observedAt","confidence","summary"]}},"conflicting_evidence":{"type":"array","items":{"type":"string"}},"sources_checked":{"type":"array","items":{"type":"string"}},"confidence_reasons":{"type":"array","items":{"type":"string"}},"signals":{"type":"object"},"insights":{"type":"array","items":{"type":"object"}},"next_steps":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object","properties":{"checked_at":{"type":"string","format":"date-time"},"cache":{"type":"object","properties":{"hit":{"type":"boolean"},"stale":{"type":"boolean","description":"True when served from stale-while-revalidate grace period."},"ttl_seconds":{"type":"integer"},"age_seconds":{"type":"integer","description":"Cache entry age in seconds."}},"required":["hit","ttl_seconds"]},"quota":{"type":"object","properties":{"limit":{"type":"integer"},"remaining":{"type":"integer"},"reset_at":{"type":"string","format":"date-time"}},"required":["limit","remaining","reset_at"]},"version":{"type":"string"}},"required":["checked_at","cache","version"]},"profile_used":{"type":"string","enum":["default","onboarding","marketplace_seller","lead_filtering","fraud_triage"]},"analysis_mode":{"type":"string","enum":["fast","standard","deep"]},"analysis_completeness":{"type":"string","enum":["complete","partial","degraded"]},"degraded":{"type":"boolean"},"degradation_reasons":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"reason":{"type":"string","enum":["budget_skipped","timeout","api_error","plan_restricted"]},"impact":{"type":"string","enum":["confidence_reduced","score_skewed","signal_missing"]}}}},"collector_budget":{"type":"object","properties":{"mode":{"type":"string","enum":["fast","standard","deep"]},"external_requests_used":{"type":"integer"},"external_requests_budget":{"type":"integer"},"skipped_collectors":{"type":"array","items":{"type":"string"}}}}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object"}},"required":["code","message"]}}},"RiskAssessment":{"type":"object","properties":{"label":{"type":"string","enum":["low","medium","high","unknown"]},"confidence":{"type":"string","enum":["low","medium","high"]},"reasons":{"type":"array","items":{"type":"string"}},"visibility":{"type":"string","enum":["preview","full"]}},"required":["label","confidence","reasons","visibility"]},"QuotaExceededError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","const":"QUOTA_EXCEEDED"},"message":{"type":"string"},"details":{"type":"object","properties":{"monthly_limit":{"type":"integer"},"remaining":{"type":"integer"},"reset_at":{"type":"string","format":"date-time"}},"required":["monthly_limit","remaining","reset_at"]}},"required":["code","message","details"]}},"required":["error"]},"AnalyzeBatchResponse":{"type":"object","required":["batch_id","total","succeeded","failed","results","metadata"],"properties":{"batch_id":{"type":"string","format":"uuid"},"total":{"type":"integer"},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"oneOf":[{"type":"object","required":["domain","normalized_domain","status","response"],"properties":{"domain":{"type":"string"},"normalized_domain":{"type":"string"},"status":{"type":"string","const":"fulfilled"},"response":{"$ref":"#/components/schemas/AnalyzeResponse"}}},{"type":"object","required":["domain","status","error"],"properties":{"domain":{"type":"string"},"normalized_domain":{"type":"string"},"status":{"type":"string","const":"rejected"},"error":{"type":"object"}}}]}},"metadata":{"type":"object","required":["checked_at","concurrency","version"],"properties":{"checked_at":{"type":"string","format":"date-time"},"concurrency":{"type":"integer"},"quota":{"type":"object","properties":{"limit":{"type":"integer"},"remaining":{"type":"integer"},"reset_at":{"type":"string","format":"date-time"}},"required":["limit","remaining","reset_at"]},"version":{"type":"string"}}}}},"WebhookSubscription":{"type":"object","required":["id","url","events","status","created_at","last_delivered_at","failure_count"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["analysis.completed"]}},"status":{"type":"string","enum":["active","revoked"]},"created_at":{"type":"string","format":"date-time"},"last_delivered_at":{"type":["string","null"],"format":"date-time"},"failure_count":{"type":"integer","minimum":0}}},"AuditLogEntry":{"type":"object","required":["id","user_id","actor_email","action","target_resource","target_id","ip","user_agent","metadata","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"user_id":{"type":["string","null"],"format":"uuid"},"actor_email":{"type":["string","null"],"format":"email"},"action":{"type":"string","enum":["api_key.created","api_key.revoked","api_key.rotated","webhook.created","webhook.deleted","plan.upgraded","plan.downgraded","quota.override_applied","login.succeeded","login.failed"]},"target_resource":{"type":"string"},"target_id":{"type":["string","null"],"format":"uuid"},"ip":{"type":["string","null"]},"user_agent":{"type":["string","null"]},"metadata":{"type":"object"},"created_at":{"type":"string","format":"date-time"}}},"ApiKeySummary":{"type":"object","required":["id","key_prefix","name","plan","monthly_limit","status","created_at","last_used_at"],"properties":{"id":{"type":"string","format":"uuid"},"key_prefix":{"type":"string"},"name":{"type":["string","null"]},"plan":{"type":"string","enum":["free","starter","growth","pro"]},"monthly_limit":{"type":"integer"},"status":{"type":"string","enum":["active","revoked"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"}}},"DetailedHealthResponse":{"type":"object","required":["ok","service","version","env","generated_at","rollup_ready","window","metrics"],"properties":{"ok":{"type":"boolean","const":true},"service":{"type":"string","const":"zygentrust-api"},"version":{"type":"string"},"env":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"rollup_ready":{"type":"boolean"},"window":{"oneOf":[{"type":"null"},{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}},"required":["start","end"]}]},"metrics":{"oneOf":[{"type":"null"},{"type":"object","required":["request_count","error_count","avg_latency_ms","p95_latency_ms","cache_hit_rate","error_rate","top_error_codes"],"properties":{"request_count":{"type":"integer"},"error_count":{"type":"integer"},"avg_latency_ms":{"type":"number"},"p95_latency_ms":{"type":"integer"},"cache_hit_rate":{"type":"number"},"error_rate":{"type":"number"},"top_error_codes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"count":{"type":"integer"}},"required":["code","count"]}}}}]}}}}},"paths":{"/v1/health":{"get":{"summary":"Service health check.","responses":{"200":{"description":"OK"}}}},"/v1/health/detailed":{"get":{"summary":"Authenticated observability health details.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"responses":{"200":{"description":"Latest daily observability rollup, or rollup_ready=false before cron runs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetailedHealthResponse"}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/analyze":{"get":{"summary":"Analyze a domain and return a trust score with explainable signals.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"domain","in":"query","required":true,"schema":{"type":"string"}},{"name":"ttl","in":"query","required":false,"description":"Growth/Pro only. Integer cache TTL in hours. Growth allows 1..72; Pro allows 1..168.","schema":{"type":"integer","minimum":1,"maximum":168}},{"name":"refresh","in":"query","required":false,"description":"Force a fresh analysis bypassing cache. Growth/Pro only.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Analysis result.","headers":{"X-RateLimit-Limit":{"description":"Monthly quota limit for the authenticated key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current monthly quota window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current monthly quota window resets.","schema":{"type":"integer"}},"Cache-Control":{"description":"CDN cache directive.","schema":{"type":"string"}},"X-Response-Time":{"description":"Request total time in ms.","schema":{"type":"string"}},"Server-Timing":{"description":"Server timing metrics.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeResponse"}}}},"400":{"description":"Invalid domain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exhausted.","headers":{"X-RateLimit-Limit":{"description":"Monthly quota limit for the authenticated key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current monthly quota window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current monthly quota window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaExceededError"}}}}}}},"/v1/analyze/batch":{"post":{"summary":"Analyze up to BATCH_MAX domains in one request.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domains"],"properties":{"domains":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Batch analysis envelope with per-domain success or error items.","headers":{"X-RateLimit-Limit":{"description":"Monthly quota limit for the authenticated key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current monthly quota window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current monthly quota window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeBatchResponse"}}}},"400":{"description":"Invalid batch request."},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."}}}},"/v1/webhooks":{"post":{"summary":"Register an HTTPS webhook subscription.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["analysis.completed"]}},"secret":{"type":"string"}}}}}},"responses":{"201":{"description":"Webhook created. Secret is returned once.","content":{"application/json":{"schema":{"type":"object","required":["webhook","secret"],"properties":{"webhook":{"$ref":"#/components/schemas/WebhookSubscription"},"secret":{"type":"string"}}}}}},"400":{"description":"Invalid webhook request."},"401":{"description":"Missing or invalid API key."}}},"get":{"summary":"List webhook subscriptions.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"responses":{"200":{"description":"Webhook list.","content":{"application/json":{"schema":{"type":"object","required":["webhooks"],"properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}}},"401":{"description":"Missing or invalid API key."}}}},"/v1/webhooks/{id}":{"delete":{"summary":"Revoke a webhook subscription.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook revoked."},"401":{"description":"Missing or invalid API key."},"404":{"description":"Webhook not found."}}}},"/v1/waitlist":{"post":{"summary":"Join the ZygenTrust waitlist (Turnstile-protected).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","turnstileToken"],"properties":{"email":{"type":"string","format":"email"},"use_case":{"type":"string"},"source":{"type":"string"},"turnstileToken":{"type":"string"}}}}}},"responses":{"200":{"description":"Accepted."},"400":{"description":"Validation or Turnstile failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/keys":{"post":{"summary":"Create a self-serve test API key for the authenticated Supabase user.","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80}},"additionalProperties":false}}}},"responses":{"201":{"description":"API key created. Secret is returned once and is never stored.","content":{"application/json":{"schema":{"type":"object","required":["api_key","secret"],"properties":{"api_key":{"$ref":"#/components/schemas/ApiKeySummary"},"secret":{"type":"string"}}}}}},"400":{"description":"Invalid key creation request."},"401":{"description":"Supabase user JWT required."}}},"get":{"summary":"List API keys for the authenticated Supabase user.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"API key list without raw secrets or hashes.","content":{"application/json":{"schema":{"type":"object","required":["api_keys"],"properties":{"api_keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeySummary"}}}}}}},"401":{"description":"Supabase user JWT required."}}}},"/v1/keys/{id}":{"delete":{"summary":"Revoke an owned API key and invalidate its KV metadata cache.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"API key revoked."},"401":{"description":"Supabase user JWT required."},"404":{"description":"API key not found for this user."}}}},"/v1/keys/{id}/rotate":{"post":{"summary":"Rotate an owned API key by revoking the old key and returning one new raw secret once.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"API key rotated. New secret is returned once and is never stored.","content":{"application/json":{"schema":{"type":"object","required":["old_api_key","api_key","secret"],"properties":{"old_api_key":{"$ref":"#/components/schemas/ApiKeySummary"},"api_key":{"$ref":"#/components/schemas/ApiKeySummary"},"secret":{"type":"string"}}}}}},"401":{"description":"Supabase user JWT required."},"404":{"description":"API key not found for this user."}}}},"/v1/audit-log":{"get":{"summary":"List audit log entries. Pro plan only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"action","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Audit log entries for the authenticated owner.","content":{"application/json":{"schema":{"type":"object","required":["audit_log","pagination"],"properties":{"audit_log":{"type":"array","items":{"$ref":"#/components/schemas/AuditLogEntry"}},"pagination":{"type":"object","required":["limit","offset","returned"],"properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"returned":{"type":"integer"}}}}}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Pro plan required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/watchlist":{"post":{"summary":"Add a domain to the authenticated API key watchlist. Growth and Pro only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"},"alert_threshold":{"type":"integer","minimum":1,"maximum":100},"webhook_url":{"type":"string","format":"uri"}}}}}},"responses":{"201":{"description":"Watchlist item created.","content":{"application/json":{"schema":{"type":"object","required":["id","api_key_id","domain","alert_threshold","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"api_key_id":{"type":"string","format":"uuid"},"domain":{"type":"string"},"last_trust_score":{"type":["integer","null"]},"last_risk_level":{"type":["string","null"]},"last_checked_at":{"type":["string","null"]},"alert_threshold":{"type":"integer"},"webhook_url":{"type":["string","null"],"format":"uri"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Invalid watchlist request."},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."}}},"get":{"summary":"List watchlist items. Growth and Pro only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Watchlist items for the authenticated API key.","content":{"application/json":{"schema":{"type":"object","required":["items","count"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","api_key_id","domain","alert_threshold","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"api_key_id":{"type":"string","format":"uuid"},"domain":{"type":"string"},"last_trust_score":{"type":["integer","null"]},"last_risk_level":{"type":["string","null"]},"last_checked_at":{"type":["string","null"]},"alert_threshold":{"type":"integer"},"webhook_url":{"type":["string","null"],"format":"uri"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}}},"count":{"type":"integer"}}}}}},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."}}}},"/v1/watchlist/{id}":{"get":{"summary":"Get one watchlist item. Growth and Pro only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Watchlist item returned."},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."},"404":{"description":"Watchlist item not found."}}},"delete":{"summary":"Delete one watchlist item. Growth and Pro only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Watchlist item deleted."},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."},"404":{"description":"Watchlist item not found."}}}},"/v1/watchlist/{id}/recheck":{"post":{"summary":"Recheck one watchlist item. Growth and Pro only.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Watchlist item rechecked with latest analysis and delta."},"401":{"description":"Missing or invalid API key."},"403":{"description":"Growth or Pro plan required."},"404":{"description":"Watchlist item not found."}}}},"/v1/analyses/recent":{"get":{"summary":"List recent analyses for scoring review.","description":"Returns recent `domain_analysis_history` rows with summary fields needed by the internal scoring dashboard.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"domain","in":"query","required":false,"schema":{"type":"string"}},{"name":"risk_level","in":"query","required":false,"schema":{"type":"string","enum":["low","medium","high"]}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Recent analysis rows.","content":{"application/json":{"schema":{"type":"object","required":["analyses","total"],"properties":{"analyses":{"type":"array","items":{"type":"object","required":["id","domain","trust_score","risk_level","recommended_action","confidence","summary","insights","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"domain":{"type":"string"},"trust_score":{"type":"integer","minimum":0,"maximum":100},"risk_level":{"type":"string","enum":["low","medium","high"]},"recommended_action":{"type":"string","enum":["approve","manual_review","reject_or_verify"]},"confidence":{"type":"number","minimum":0,"maximum":1},"summary":{"type":"string"},"insights":{"type":"array","items":{"type":"object"}},"created_at":{"type":"string","format":"date-time"}}}},"total":{"type":"integer"}}}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/analyses/{id}/feedback":{"post":{"summary":"Store scoring feedback for one analysis.","description":"Stores a `good` or `bad` feedback label and optional notes for an existing analysis history row.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["feedback"],"properties":{"feedback":{"type":"string","enum":["good","bad"]},"notes":{"type":"string","maxLength":2000}}}}}},"responses":{"200":{"description":"Feedback saved.","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","const":true}}}}}},"400":{"description":"Invalid request body or path parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Analysis not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/jobs":{"post":{"summary":"Create an async batch analysis job (Growth/Pro only).","description":"Creates an async batch job. Returns a job_id immediately. Domains are processed via Cloudflare Queues. Poll GET /v1/jobs/:id for status. Requires a Growth or Pro API key. Quota is consumed one unit per domain at creation time.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domains"],"properties":{"domains":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100,"description":"Domains to analyze (1-100)."},"profile":{"type":"string","enum":["default","onboarding","marketplace_seller","lead_filtering","fraud_triage"],"default":"default"},"mode":{"type":"string","enum":["fast","standard","deep"],"default":"standard"},"webhook_url":{"type":"string","format":"uri","description":"HTTPS URL for completion webhook."},"requested_format":{"type":"string","enum":["json","csv"],"default":"json"}}}}}},"responses":{"202":{"description":"Job created successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]},"total_count":{"type":"integer"},"poll_url":{"type":"string"},"results_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}}},"headers":{"X-RateLimit-Limit":{"description":"Monthly quota limit for the authenticated key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current monthly quota window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current monthly quota window resets.","schema":{"type":"integer"}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Growth or Pro plan required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaExceededError"}}}}}}},"/v1/jobs/{job_id}":{"get":{"summary":"Poll async job status.","description":"Returns current status of an async analysis job. Poll every 2-5 seconds. Do not poll faster than once per second.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Job status retrieved.","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"string"},"status":{"type":"string","enum":["queued","processing","completed","failed","partial"]},"profile":{"type":"string"},"mode":{"type":"string"},"total_count":{"type":"integer"},"completed_count":{"type":"integer"},"failed_count":{"type":"integer"},"progress":{"type":"number"},"created_at":{"type":"string","format":"date-time"},"started_at":{"type":"string","format":"date-time","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true}}}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/jobs/{job_id}/results":{"get":{"summary":"Get async job results.","description":"Returns completed items from an async analysis job. Supports JSON and CSV output via format query parameter.","security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"],"default":"json"}}],"responses":{"200":{"description":"Job results.","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"string"},"status":{"type":"string"},"total_count":{"type":"integer"},"completed_count":{"type":"integer"},"failed_count":{"type":"integer"},"items":{"type":"array","items":{"type":"object","properties":{"domain":{"type":"string"},"normalized_domain":{"type":"string"},"status":{"type":"string"},"trust_score":{"type":"integer"},"risk_level":{"type":"string"},"recommended_action":{"type":"string"},"confidence":{"type":"number"},"result_json":{"$ref":"#/components/schemas/AnalyzeResponse"},"error_code":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true}}}},"metadata":{"type":"object","properties":{"checked_at":{"type":"string"},"version":{"type":"string"},"profile":{"type":"string"},"mode":{"type":"string"}}}}}}}},"401":{"description":"API key required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/owner/me":{"get":{"tags":["Owner Control Plane"],"summary":"Get current owner profile.","description":"Returns the authenticated owner's profile and email. Requires owner role.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Owner profile returned.","content":{"application/json":{"schema":{"type":"object","properties":{"owner":{"type":"object","properties":{"user_id":{"type":"string"},"role":{"type":"string"},"display_name":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"last_login_at":{"type":"string"}}},"email":{"type":"string"}}}}}},"401":{"description":"Missing or invalid JWT.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not an owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/owner/overview":{"get":{"tags":["Owner Control Plane"],"summary":"Dashboard overview metrics.","description":"Returns counts of users, keys, requests, errors, feedback, jobs, and upgrade requests.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Overview metrics.","content":{"application/json":{"schema":{"type":"object","properties":{"overview":{"type":"object","properties":{"total_users":{"type":"integer"},"active_api_keys":{"type":"integer"},"requests_today":{"type":"integer"},"requests_this_month":{"type":"integer"},"error_rate_percent":{"type":"number"},"upgrade_requests_pending":{"type":"integer"},"feedback_count":{"type":"integer"},"async_jobs_today":{"type":"integer"},"generated_at":{"type":"string"}}}}}}}}}}},"/v1/owner/users":{"get":{"tags":["Owner Control Plane"],"summary":"List all users.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User list with API key counts.","content":{"application/json":{"schema":{"type":"object","properties":{"users":{"type":"array"}}}}}}}}},"/v1/owner/users/{id}":{"get":{"tags":["Owner Control Plane"],"summary":"Get user detail.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User profile, API keys, and upgrade requests.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"User not found."}}}},"/v1/owner/users/{id}/plan":{"put":{"tags":["Owner Control Plane"],"summary":"Change user plan.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string"},"monthly_limit":{"type":"integer"}},"required":["plan","monthly_limit"]}}}},"responses":{"200":{"description":"Plan updated."},"400":{"description":"Invalid request."}}}},"/v1/owner/keys/{id}/revoke":{"post":{"tags":["Owner Control Plane"],"summary":"Revoke any API key by ID.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Key revoked."},"404":{"description":"Key not found."}}}},"/v1/owner/upgrade-requests":{"get":{"tags":["Owner Control Plane"],"summary":"List upgrade requests.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Upgrade requests list.","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array"}}}}}}}}},"/v1/owner/feedback":{"get":{"tags":["Owner Control Plane"],"summary":"List scoring feedback.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Feedback list.","content":{"application/json":{"schema":{"type":"object","properties":{"feedback":{"type":"array"}}}}}}}}},"/v1/owner/abuse/overview":{"get":{"tags":["Abuse Detection"],"summary":"Abuse detection overview.","description":"Returns severity counts and recent active flags.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Abuse overview with critical/warning/info counts.","content":{"application/json":{"schema":{"type":"object","properties":{"overview":{"type":"object"}}}}}}}}},"/v1/owner/abuse/events":{"get":{"tags":["Abuse Detection"],"summary":"Recent abuse events.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of abuse events.","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array"}}}}}}}}},"/v1/owner/abuse/flags":{"get":{"tags":["Abuse Detection"],"summary":"List all abuse flags.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Abuse flags list.","content":{"application/json":{"schema":{"type":"object","properties":{"flags":{"type":"array"}}}}}}}}},"/v1/owner/abuse/detect":{"post":{"tags":["Abuse Detection"],"summary":"Run abuse detection.","description":"Runs the detection engine and persists new flags. Deduplicates against active flags.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Detection results.","content":{"application/json":{"schema":{"type":"object","properties":{"detection":{"type":"object","properties":{"detected":{"type":"integer"},"inserted":{"type":"integer"},"skipped":{"type":"integer"}}}}}}}}}}},"/v1/owner/abuse/flags/{id}":{"put":{"tags":["Abuse Detection"],"summary":"Update abuse flag status.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["active","dismissed","escalated","resolved"]},"notes":{"type":"string"}},"required":["status"]}}}},"responses":{"200":{"description":"Flag updated."},"404":{"description":"Flag not found."}}}},"/v1/owner/abuse/users/{id}/flag":{"post":{"tags":["Abuse Detection"],"summary":"Manually flag a user.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"flag_type":{"type":"string"},"severity":{"type":"string","enum":["info","warning","critical"]},"notes":{"type":"string"}},"required":["flag_type","severity"]}}}},"responses":{"200":{"description":"User flagged."}}}},"/v1/owner/jobs":{"get":{"tags":["Jobs Monitoring"],"summary":"List all async analysis jobs.","security":[{"bearerAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["queued","processing","completed","failed","partial"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Job list with status counts.","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array"},"stats":{"type":"object"}}}}}}}}},"/v1/owner/jobs/{id}":{"get":{"tags":["Jobs Monitoring"],"summary":"Get job detail with items.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job detail with items."},"404":{"description":"Job not found."}}}},"/v1/owner/jobs/{id}/cancel":{"post":{"tags":["Jobs Monitoring"],"summary":"Cancel a queued/processing job.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job cancelled."},"400":{"description":"Job cannot be cancelled."}}}},"/v1/owner/settings":{"get":{"tags":["Owner Settings"],"summary":"Get owner settings.","description":"Returns all settings from owner_settings table with env defaults as fallback.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Settings object.","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object"}}}}}}}},"put":{"tags":["Owner Settings"],"summary":"Update owner settings.","description":"Upserts settings. Dangerous changes (runtime_tier, allow_public_analyze, deep_mode_enabled) require 'confirmation: CONFIRM' in the body.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"runtime_tier":{"type":"string","enum":["free","paid"]},"allow_public_analyze":{"type":"boolean"},"deep_mode_enabled":{"type":"boolean"},"batch_max_growth":{"type":"integer"},"batch_max_pro":{"type":"integer"},"confirmation":{"type":"string","description":"Required for dangerous changes. Must be 'CONFIRM'."}}}}}},"responses":{"200":{"description":"Settings updated."},"400":{"description":"Confirmation required for dangerous changes."}}}},"/v1/owner/setup":{"post":{"tags":["Owner Control Plane"],"summary":"Bootstrap first owner account.","description":"One-time setup. Requires OWNER_BOOTSTRAP_TOKEN. Fails if owner already exists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"email":{"type":"string"},"password":{"type":"string","minLength":8},"display_name":{"type":"string"}},"required":["token","email","password"]}}}},"responses":{"201":{"description":"Owner account created."},"409":{"description":"Owner already configured."},"503":{"description":"Bootstrap not configured."}}}}}}