{"openapi":"3.1.0","info":{"title":"Voidek API","version":"0.1.0","description":"Crawler, scraper, and headless browser defense API. First create a personal account, add one protected site, save the one-time runtime key as VOIDEK_API_KEY, run a controlled test decision, then compare sample rows with live Analytics before enforcing routes."},"servers":[{"url":"https://edge.voidek.dev","description":"Production runtime API for customer protected site traffic"},{"url":"https://voidek.dev/api","description":"First-party dashboard and admin management API surface"}],"tags":[{"name":"Runtime","description":"Protected site runtime calls used by snippets, middleware, and test decisions. Keep VOIDEK_API_KEY server-side."},{"name":"Dashboard","description":"Authenticated account, protected site setup, one-time key setup, install snippets, logs, usage, and billing management."},{"name":"Public","description":"Unauthenticated status and billing-interest endpoints."}],"components":{"securitySchemes":{"RuntimeApiKey":{"type":"http","scheme":"bearer","bearerFormat":"Voidek runtime key","description":"Use `Authorization: Bearer voidek-live-...` from server-side middleware or the dashboard test request only. Do not expose VOIDEK_API_KEY in browser JavaScript."},"SessionCookie":{"type":"apiKey","in":"cookie","name":"vd_auth","description":"HttpOnly dashboard session cookie set by signup, login, or OAuth."}},"schemas":{"HeadersInput":{"type":"object","additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}},{"type":"null"}]}},"DecideRequest":{"type":"object","required":["siteId","path"],"properties":{"siteId":{"type":"string","description":"Protected site ID from the dashboard.","examples":["site_123"]},"path":{"type":"string","examples":["protected page"]},"method":{"type":"string","default":"GET"},"ip":{"type":"string","examples":["203.0.113.10"]},"headers":{"$ref":"#/components/schemas/HeadersInput"},"challengeToken":{"type":"string"},"challengePassed":{"type":"boolean"},"cookiesSupported":{"type":"boolean"},"webdriver":{"type":"boolean"},"recentRequestCount":{"type":"integer","minimum":0},"sameUserAgentIpCount":{"type":"integer","minimum":0},"sameRouteIpCount":{"type":"integer","minimum":0},"pathBurstCount":{"type":"integer","minimum":0},"headerShapeCount":{"type":"integer","minimum":0},"missingSessionContinuity":{"type":"boolean"},"providerChangeCount":{"type":"integer","minimum":0},"proxyHeaderCount":{"type":"integer","minimum":0},"provider":{"type":"string","enum":["aws","azure","gcp","googlebot"]},"cloudProvider":{"type":"string","enum":["aws","azure","gcp"]},"networkProvider":{"type":"string","enum":["aws","azure","gcp","googlebot"]},"searchCrawlerVerified":{"type":"boolean"},"ipReputation":{"type":"string","enum":["residential","datacenter","cloud","tor","anonymous-proxy"]}}},"Decision":{"type":"object","properties":{"action":{"type":"string","enum":["allow","log","challenge","block"]},"score":{"type":"integer"},"botType":{"type":"string"},"recommendedAction":{"type":"string","enum":["allow","log","challenge","block"],"description":"Raw scorer recommendation before site mode is applied."},"mode":{"type":"string","enum":["monitor","enforce"],"description":"Site mode used for the effective action."},"enforced":{"type":"boolean","description":"False when monitor mode converted a risky recommendation into a log-only action."},"confidence":{"type":"string","enum":["none","low","medium","high"]},"reasons":{"type":"array","items":{}},"tags":{"type":"array","items":{"type":"string"}},"provider":{"$ref":"#/components/schemas/NetworkProvider"},"providerReason":{"type":"string"},"policy":{"$ref":"#/components/schemas/DefensePolicy"},"policyReason":{"type":"string"}}},"NetworkProvider":{"type":"object","properties":{"id":{"type":"string","enum":["aws","azure","gcp","googlebot"]},"label":{"type":"string"},"kind":{"type":"string","enum":["cloud","verified-search-crawler"]},"source":{"type":"string","enum":["provided","headers","ip-fixture"]}}},"DefensePolicy":{"type":"object","properties":{"preset":{"type":"string","enum":["monitor","balanced","strict","custom"]},"actionPreference":{"type":"string","enum":["challenge","block"]},"toggles":{"type":"object","properties":{"aiCrawlers":{"type":"boolean"},"verifiedSearchCrawlers":{"type":"boolean"},"cloudProviderTraffic":{"type":"boolean"},"headlessBrowsers":{"type":"boolean"},"scraperLibraries":{"type":"boolean"},"proxySuspicion":{"type":"boolean"},"rateSpikes":{"type":"boolean"}}}}},"AllowlistOverride":{"type":"object","properties":{"id":{"type":"string"},"siteId":{"type":"string"},"kind":{"type":"string","enum":["ip","cidr","userAgent","path","provider","botType"]},"value":{"type":"string"},"action":{"type":"string","enum":["allow"]},"note":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"AllowlistOverrideRequest":{"type":"object","required":["siteId","kind","value"],"properties":{"siteId":{"type":"string"},"kind":{"type":"string","enum":["ip","cidr","userAgent","path","provider","botType"]},"value":{"type":"string"},"note":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}}},"ReviewStatusUpdate":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["new","reviewing","allowed","dismissed"]}}},"CollectRequest":{"type":"object","required":["siteId","token"],"properties":{"siteId":{"type":"string","description":"Protected site ID embedded in the browser snippet."},"token":{"type":"string"},"webdriver":{"type":"boolean"},"language":{"type":"string"},"timezone":{"type":"string"},"screen":{"type":"string"},"userAgent":{"type":"string"},"platform":{"type":"string"},"vendor":{"type":"string"},"hardwareConcurrency":{"type":"integer"},"deviceMemory":{"type":"number"},"maxTouchPoints":{"type":"integer"},"pluginsLength":{"type":"integer"},"hasChromeRuntime":{"type":"boolean"}}},"AuthRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8},"name":{"type":"string"}}},"SiteCreateRequest":{"type":"object","required":["name","domain"],"properties":{"name":{"type":"string","description":"Protected site name shown in the dashboard.","examples":["Docs Production"]},"domain":{"type":"string","description":"Hostname or URL for the protected site. Voidek stores the normalized hostname.","examples":["docs.example.com"]},"mode":{"type":"string","enum":["monitor","enforce"],"default":"enforce","description":"Monitor logs risky traffic without enforcing. Enforce returns challenge/block recommendations as the effective action."},"defensePolicy":{"$ref":"#/components/schemas/DefensePolicy"}}},"SiteUpdateRequest":{"type":"object","properties":{"name":{"type":"string","description":"Optional protected site display name update."},"mode":{"type":"string","enum":["monitor","enforce"],"description":"Switch between log-only monitor mode and enforcing challenge/block decisions."},"defensePolicy":{"$ref":"#/components/schemas/DefensePolicy"}}},"BillingIntentRequest":{"type":"object","required":["plan","email"],"properties":{"plan":{"type":"string","enum":["starter","growth","pro"]},"billingCycle":{"type":"string","enum":["monthly","annual"],"default":"monthly"},"email":{"type":"string","format":"email"},"site":{"type":"string","description":"Optional protected site domain or URL for the billing request."},"trafficRange":{"type":"string","description":"Estimated monthly request volume for the protected site or account."},"note":{"type":"string","description":"Optional context about protected routes, crawler pressure, or billing needs."},"source":{"type":"string","description":"Source surface that submitted the billing request."}}},"BillingCheckoutRequest":{"type":"object","required":["plan","billingCycle"],"properties":{"plan":{"type":"string","enum":["starter","growth"]},"billingCycle":{"type":"string","enum":["monthly","annual"]}}},"BillingCheckoutCapability":{"type":"object","properties":{"enabled":{"type":"boolean","description":"True only after provider env, plan checkout URL, and VOIDEK_BILLING_CHECKOUT_ENABLED=true are all set."},"provider":{"type":"string","enum":["none","stripe","paddle","lemon_squeezy"]},"mode":{"type":"string","enum":["reserve","hosted_checkout_link"]},"reserveReason":{"type":"string","enum":["provider_not_configured","mvp_validation_pending","checkout_url_missing"]},"message":{"type":"string"}}},"ContactRequest":{"type":"object","required":["email","request"],"properties":{"email":{"type":"string","format":"email"},"site":{"type":"string","description":"Optional protected site URL or hostname."},"request":{"type":"string","description":"Rollout, integration, Pro pricing, or support context."}}}}},"paths":{"/health":{"get":{"tags":["Public"],"summary":"Health check","responses":{"200":{"description":"API status","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"}}}}}}}}},"/v1/decide":{"post":{"tags":["Runtime"],"summary":"Score one protected request or test decision","security":[{"RuntimeApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideRequest"}}}},"responses":{"201":{"description":"Decision result for Analytics and route enforcement","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Decision"}}}},"401":{"description":"Missing or invalid bearer runtime key when key enforcement is enabled"}}}},"/v1/collect":{"post":{"tags":["Runtime"],"summary":"Store browser proof from the hosted snippet","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectRequest"}}}},"responses":{"201":{"description":"Browser proof accepted"},"404":{"description":"Unknown protected site ID"}}}},"/v1/auth/signup":{"post":{"tags":["Dashboard"],"summary":"Create a personal account","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthRequest"}}}},"responses":{"201":{"description":"Session cookie set and user returned"},"409":{"description":"Email already registered"}}}},"/v1/auth/login":{"post":{"tags":["Dashboard"],"summary":"Login with email and password","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthRequest"}}}},"responses":{"201":{"description":"Session cookie set"},"401":{"description":"Invalid credentials"}}}},"/v1/auth/logout":{"post":{"tags":["Dashboard"],"summary":"Delete the current session","security":[{"SessionCookie":[]}],"responses":{"201":{"description":"Session cleared"}}}},"/v1/auth/me":{"get":{"tags":["Dashboard"],"summary":"Return current user and account","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Authenticated profile"},"401":{"description":"No active session"}}}},"/v1/sites":{"get":{"tags":["Dashboard"],"summary":"List account protected sites","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Protected sites for the current account"}}},"post":{"tags":["Dashboard"],"summary":"Add a protected site and receive the one-time runtime key","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteCreateRequest"}}}},"responses":{"201":{"description":"Protected site, masked runtime-key metadata, one-time runtime key, and install snippets"},"409":{"description":"Domain already belongs to another account"}}}},"/v1/sites/{id}/install":{"get":{"tags":["Dashboard"],"summary":"Get install snippets for a protected site","security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Install snippets"},"404":{"description":"Protected site not found"}}}},"/v1/sites/{id}":{"patch":{"tags":["Dashboard"],"summary":"Update protected site settings","description":"Update the protected site's display name or switch rollout mode. Monitor mode converts risky challenge/block recommendations into log-only decisions; enforce mode returns the scorer action.","security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteUpdateRequest"}}}},"responses":{"200":{"description":"Updated protected site and install snippets"},"400":{"description":"Invalid name or site mode"},"401":{"description":"Dashboard session required"},"404":{"description":"Protected site not found"}}}},"/v1/logs":{"get":{"tags":["Dashboard"],"summary":"List recent request logs shown in Analytics","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Recent logs for confirming test decisions and live crawler activity"}}}},"/v1/review":{"get":{"tags":["Dashboard"],"summary":"List false-positive review rows and active allowlist overrides","security":[{"SessionCookie":[]}],"parameters":[{"name":"siteId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Review rows and active allowlist overrides for the authenticated user's protected sites"},"401":{"description":"Dashboard session required"},"404":{"description":"Protected site not found"}}}},"/v1/review/{id}":{"patch":{"tags":["Dashboard"],"summary":"Update a request log review status","security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewStatusUpdate"}}}},"responses":{"200":{"description":"Updated review log"},"400":{"description":"Invalid review status"},"401":{"description":"Dashboard session required"},"404":{"description":"Request log not found"}}}},"/v1/allowlist":{"post":{"tags":["Dashboard"],"summary":"Create an allowlist override for known false positives","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowlistOverrideRequest"}}}},"responses":{"201":{"description":"Allowlist override created","content":{"application/json":{"schema":{"type":"object","properties":{"override":{"$ref":"#/components/schemas/AllowlistOverride"}}}}}},"400":{"description":"Invalid override request"},"401":{"description":"Dashboard session required"},"404":{"description":"Protected site not found"}}}},"/v1/usage":{"get":{"tags":["Dashboard"],"summary":"Read monthly site usage and quota","security":[{"SessionCookie":[]}],"parameters":[{"name":"siteId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Usage against account plan quota"}}}},"/v1/billing":{"get":{"tags":["Dashboard"],"summary":"Read current account plan, billing history, and checkout readiness","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Account plan, billing history, and checkout capability. Before MVP validation completes, checkout remains reserve-only."}}}},"/v1/billing/checkout":{"post":{"tags":["Dashboard"],"summary":"Reserve or start paid plan checkout","description":"Before live billing is approved, this endpoint records a reserved billing intent and returns `checkout.enabled=false`. Hosted checkout URLs are returned only when provider env, plan URL env, and `VOIDEK_BILLING_CHECKOUT_ENABLED=true` are configured.","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingCheckoutRequest"}}}},"responses":{"201":{"description":"Reserved setup request or hosted checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"boolean"},"checkout":{"$ref":"#/components/schemas/BillingCheckoutCapability"},"intent":{"description":"Present when checkout is reserve-only."},"account":{"description":"Current account plan remains unchanged until a verified provider webhook updates it."}}}}}},"400":{"description":"Invalid plan or billing cycle"},"401":{"description":"Dashboard session required"}}}},"/v1/account":{"patch":{"tags":["Dashboard"],"summary":"Update account display name","security":[{"SessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated account"}}}},"/v1/admin/overview":{"get":{"tags":["Dashboard"],"summary":"Read admin-only operations, contact, traffic, and marketing summary","description":"Admin role required. Returns account/site counts, recent contact and billing intents, protected-site request volume, top paths/actions/clients, and lightweight marketing funnel metrics for the operator dashboard.","security":[{"SessionCookie":[]}],"responses":{"200":{"description":"Admin overview metrics and recent records"},"401":{"description":"Admin role required"}}}},"/v1/billing-intents":{"post":{"tags":["Public"],"summary":"Save paid plan interest before payments are live","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingIntentRequest"}}}},"responses":{"201":{"description":"Paid plan request saved"}}}},"/v1/contact":{"post":{"tags":["Public"],"summary":"Save a support or rollout contact request","description":"The /contact page and landing page #contact form use this endpoint. If it is unavailable, customers can email voidek.dev@gmail.com directly.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}}},"responses":{"201":{"description":"Contact request saved"},"400":{"description":"Invalid email or missing request details"}}}}}}