{"components":{"schemas":{"Error":{"properties":{"error":{"properties":{"code":{"type":"string"},"message":{"type":"string"}},"type":"object"}},"type":"object"}},"securitySchemes":{"bearer":{"description":"API key beginning with ozy_","scheme":"bearer","type":"http"}}},"info":{"description":"Free developer APIs written in Rust. Example responses are illustrative.","title":"OZY Developer Platform","version":"0.1.0"},"openapi":"3.1.0","paths":{"/api/v1/email/validate":{"post":{"description":"Validates syntax, then resolves MX, A/AAAA, SPF and DMARC records to produce domain-level deliverability indicators. Flags disposable providers, role accounts and likely typos of major providers. It never connects to mail servers or probes individual mailboxes.\n\nRuntime: Runs on Vercel. Fully served by the Rust function on Vercel (DNS only).","operationId":"email","parameters":[],"requestBody":{"content":{"application/json":{"example":{"email":"someone@gmial.com"},"schema":{"properties":{"email":{"description":"Address to validate (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"deliverability":"domain_has_no_mail_records","did_you_mean":"someone@gmail.com","disposable":false,"dmarc_record":false,"domain":"gmial.com","email":"someone@gmial.com","mailbox_verified":false,"mx_records":[],"role_account":false,"spf_record":false,"syntax_valid":true}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Email Validation","tags":["Runs on Vercel"],"x-ozy-permission":"email","x-ozy-units":1}},"/api/v1/extract/create":{"post":{"description":"Saves a named extraction (URL + OzyScrape rules) for permitted public data and runs it immediately. The result is cached and served from GET /api/v1/extract/{id}; refreshes happen on a schedule (5 minutes to 24 hours) or on demand with ?refresh=true (at most once a minute). Usage counters track reads and refreshes.\n\nRuntime: Runs on Vercel. Runs on Vercel. Scheduled refreshes are triggered by /api/v1/internal/tick.","operationId":"extract","parameters":[],"requestBody":{"content":{"application/json":{"example":{"name":"Rust releases","refresh_seconds":3600,"rules":[{"name":"titles","selector":"table a"}],"url":"https://blog.rust-lang.org/"},"schema":{"properties":{"markdown":{"description":"Include Markdown in results (boolean)"},"name":{"description":"Display name (1-80 bytes) (string)"},"refresh_seconds":{"description":"300-86400 (default 3600) (integer)"},"rules":{"description":"OzyScrape rules (see Web Scraper) (object[])"},"url":{"description":"Public page to extract from (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"endpoint":"https://dev.itsozy.lol/api/v1/extract/9b0e…","id":"9b0e…","name":"Rust releases","result":{"fields":{"titles":{"match_type":"exact","values":["…"]}}},"usage":{"fetch_count":0,"refresh_count":1}}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Website to API","tags":["Runs on Vercel"],"x-ozy-permission":"extract","x-ozy-units":1}},"/api/v1/intelligence":{"get":{"description":"Fetches one page and applies about 50 deterministic fingerprints to HTML, script sources, the generator meta tag and response headers. Each technology carries a combined confidence and the exact evidence that matched. TLS details (subject, issuer, validity, SANs, protocol and cipher) come from a verified handshake to the same validated address.\n\nRuntime: Runs on Vercel. Fully served by the Rust function on Vercel.","operationId":"intelligence","parameters":[{"description":"Public http(s) URL","in":"query","name":"url","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"technologies":[{"category":"Frontend framework","confidence":1.0,"evidence":[{"key":"/_next/","source":"html"}],"name":"Next.js"},{"category":"Hosting","confidence":1.0,"evidence":[{"key":"x-vercel-id","source":"header"}],"name":"Vercel"}],"tls":{"certificate":{"days_remaining":61,"issuer":"C=US, O=Let's Encrypt, CN=R11","protocol":"TLSv1_3"},"https":true},"url":"https://vercel.com/"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Website Intelligence","tags":["Runs on Vercel"],"x-ozy-permission":"intelligence","x-ozy-units":1}},"/api/v1/monitor":{"post":{"description":"Creates a persistent monitor that checks availability, response time and visible-content changes on a schedule (every 5 minutes to 24 hours). The first check runs immediately. Transitions emit monitor.down, monitor.up and monitor.content_changed notifications, and optional HMAC-signed webhooks. History and uptime percentages are kept for 30 days.\n\nRuntime: Runs on Vercel. Checks run inside the Rust function. Scheduled checks are triggered by /api/v1/internal/tick (Supabase pg_cron every minute when configured; Vercel daily cron as fallback).","operationId":"monitor","parameters":[],"requestBody":{"content":{"application/json":{"example":{"interval_seconds":300,"selector":"main","url":"https://example.com"},"schema":{"properties":{"interval_seconds":{"description":"300-86400 (default 300) (integer)"},"selector":{"description":"CSS selector; content changes are detected on its visible text only (string)"},"url":{"description":"Public http(s) URL to monitor (string)"},"webhook_url":{"description":"Public https URL for signed event deliveries (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"history":[{"available":true,"changed":false,"duration_ms":182,"status":200}],"monitor":{"id":"6f1c…","interval_seconds":300,"last_available":true,"last_status":200,"url":"https://example.com/"},"stats":{"average_response_ms_24h":182,"checks":1,"uptime_24h":100.0}}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Website Monitor","tags":["Runs on Vercel"],"x-ozy-permission":"monitor","x-ozy-units":1}},"/api/v1/ocr/captcha":{"post":{"description":"Recognises text in PNG, JPEG or WebP images you own or are authorised to test. Images are converted to grayscale and optionally resized and binarised (Otsu or fixed threshold) in Rust and recognised with Tesseract through Rust bindings. Up to 5 images per request. This endpoint does not bypass third-party CAPTCHA services and makes no compatibility claims.\n\nRuntime: Requires worker. Tesseract's native libraries cannot run inside Vercel functions, so recognition runs on the optional isolated worker. Without OZY_WORKER_URL this endpoint returns 503 worker_unavailable.","operationId":"ocr","parameters":[],"requestBody":{"content":{"application/json":{"example":{"authorized_testing":true,"images":["iVBORw0KGgo..."],"options":{"psm":7,"scale":2,"threshold":"otsu"}},"schema":{"properties":{"authorized_testing":{"description":"Must be true: confirms the images are yours or from an authorised test environment (boolean)"},"images":{"description":"1-5 base64 images (PNG/JPEG/WebP, max 1 MiB each). Or send multipart files with an `options` JSON part (string[])"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"engine":"tesseract","formats":["png"],"results":[{"confidence":87.4,"height":120,"text":"X7K2P","width":400}]}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"CAPTCHA Image Recognition","tags":["Requires worker"],"x-ozy-permission":"ocr","x-ozy-units":10}},"/api/v1/pdf/parse":{"post":{"description":"Parses PDFs up to 3 MiB in pure Rust: document metadata, per-page text, and JSON or Markdown output. Parsing runs on an isolated blocking thread with a 25-second limit; malformed files fail safely. Pages with almost no text are flagged as scanned; with `ocr: true`, their embedded JPEG images are sent to the worker for Tesseract OCR.\n\nRuntime: Vercel + optional worker. Text and metadata extraction run on Vercel. OCR of scanned pages needs the optional worker; without it, `ocr_status` reports worker_unavailable and text pages are still returned.","operationId":"pdf","parameters":[],"requestBody":{"content":{"application/json":{"example":{"format":"markdown","url":"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"},"schema":{"properties":{"data":{"description":"Base64 PDF (string)"},"file":{"description":"The PDF (or use data / url) (multipart file)"},"format":{"description":"json (default) or markdown (string)"},"max_pages":{"description":"1-200 (default 50) (integer)"},"ocr":{"description":"OCR scanned pages through the worker (boolean)"},"url":{"description":"Public URL of a PDF (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"markdown":"# dummy\n\n## Page 1\n\nDummy PDF file\n\n","metadata":{"title":"dummy"},"ocr_status":"not_needed","page_count":1,"pages_returned":1,"scanned_pages":0,"version":"1.4"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"PDF Parser","tags":["Vercel + optional worker"],"x-ozy-permission":"pdf","x-ozy-units":10}},"/api/v1/performance":{"post":{"description":"By default measures a real HTTP fetch from the Vercel region: total time, time to first byte, HTML bytes, redirect chain and declared sub-resources. Responses state `measurement: \"http_fetch\"`. With `browser: true` the worker loads the page in Chromium and returns Navigation Timing and paint metrics, labelled `measurement: \"browser\"`.\n\nRuntime: Vercel + optional worker. HTTP timing runs on Vercel. Browser metrics need the optional worker.","operationId":"performance","parameters":[],"requestBody":{"content":{"application/json":{"example":{"url":"https://example.com"},"schema":{"properties":{"browser":{"description":"Real browser metrics via the worker (boolean)"},"url":{"description":"Public http(s) URL (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"browser_metrics":null,"declared_resource_count":0,"elapsed_ms":241,"first_byte_ms":198,"html_bytes":1256,"measurement":"http_fetch","redirect_count":0,"status":200,"url":"https://example.com/"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Website Performance","tags":["Vercel + optional worker"],"x-ozy-permission":"performance","x-ozy-units":1}},"/api/v1/preview":{"get":{"description":"Fetches a page with robots.txt respected and returns normalised preview metadata with absolute URLs, plus Open Graph, Twitter card and article meta tags.\n\nRuntime: Runs on Vercel. Fully served by the Rust function on Vercel.","operationId":"preview","parameters":[{"description":"Public http(s) URL","in":"query","name":"url","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"metadata":{"canonical_url":"https://github.com/","description":"…","favicon":"https://github.githubassets.com/favicons/favicon.svg","image":"https://github.githubassets.com/…png","language":"en","site_name":"GitHub","title":"GitHub"},"public_metadata":{"og:type":"object"},"status":200,"url":"https://github.com/"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Link Preview","tags":["Runs on Vercel"],"x-ozy-permission":"preview","x-ozy-units":1}},"/api/v1/scrape":{"post":{"description":"Fetches a page (respecting robots.txt and per-domain pacing) or parses supplied HTML, then applies up to 20 extraction rules. Rules support CSS selectors and a documented XPath subset, returning text, HTML or attributes. Adaptive rules store a structural fingerprint; when the selector later stops matching, OzyScrape scores candidates by tag, text, attributes, classes, ancestry, neighbours and sibling position, and returns `recovered` only above a confidence and margin threshold, otherwise `uncertain` with no values. Concurrent crawling is available through /api/v1/crawls.\n\nRuntime: Vercel + optional worker. Fetching and extraction run on Vercel. `render: true` (JavaScript rendering) requires the optional browser worker.","operationId":"scrape","parameters":[],"requestBody":{"content":{"application/json":{"example":{"markdown":true,"rules":[{"name":"heading","selector":"h1"},{"adaptive":true,"attribute":"href","kind":"attribute","name":"link","selector":"//a"}],"url":"https://example.com"},"schema":{"properties":{"cache_seconds":{"description":"Cache the result for up to 3600 s (integer)"},"html":{"description":"Inline HTML up to 2 MiB (string)"},"markdown":{"description":"Include a Markdown rendering of the body (boolean)"},"render":{"description":"Render JavaScript in the worker before extraction (boolean)"},"rules":{"description":"{name, selector, kind: text|html|attribute, attribute?, adaptive?} (object[])"},"url":{"description":"Page to fetch (exactly one of url or html) (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"cached":false,"fields":{"heading":{"candidate_margin":1.0,"confidence":1.0,"fingerprint":null,"match_type":"exact","values":["Example Domain"]}},"markdown":"Example Domain\n==============\n…","metadata":{"title":"Example Domain"},"status":200,"url":"https://example.com/"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Web Scraper","tags":["Vercel + optional worker"],"x-ozy-permission":"scrape","x-ozy-units":1}},"/api/v1/screenshot":{"post":{"description":"Captures a page in headless Chromium driven over the Chrome DevTools Protocol from Rust. Supports desktop (1440×900) and mobile (390×844 @2x) presets, custom viewports, full-page capture and PNG/JPEG/WebP output. All browser traffic passes through the worker's Rust egress proxy, which blocks private and metadata addresses.\n\nRuntime: Requires worker. Chromium cannot run reliably inside Vercel functions; captures run on the optional worker. Without it this endpoint returns 503 worker_unavailable.","operationId":"screenshot","parameters":[],"requestBody":{"content":{"application/json":{"example":{"device":"mobile","format":"webp","full_page":true,"url":"https://example.com"},"schema":{"properties":{"device":{"description":"desktop (default) or mobile (string)"},"format":{"description":"png (default), jpeg or webp (string)"},"full_page":{"description":"Capture the full scroll height (boolean)"},"quality":{"description":"1-100 for jpeg/webp (default 80) (integer)"},"url":{"description":"Public http(s) URL (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"bytes":48211,"final_url":"https://example.com/","format":"webp","height":1688,"image_base64":"UklGR…","width":390}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Screenshot API","tags":["Requires worker"],"x-ozy-permission":"screenshot","x-ozy-units":10}},"/api/v1/url/check":{"post":{"description":"Scores a URL with explainable static heuristics (credentials in the authority, IP hosts, punycode, risky TLDs, shorteners, credential-lure keywords, length and encoding), resolves its registrable domain from the Public Suffix List, and optionally follows redirects safely to detect downgrades and cross-domain hops. Every point carries a reason. No reputation or malware database is used, and a low score is not a safety guarantee.\n\nRuntime: Runs on Vercel. Fully served by the Rust function on Vercel.","operationId":"url_check","parameters":[],"requestBody":{"content":{"application/json":{"example":{"url":"http://secure-login.example-account.top/verify"},"schema":{"properties":{"follow_redirects":{"description":"Fetch and inspect redirects (default true) (boolean)"},"url":{"description":"URL to analyse (string)"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"domain":{"host":"secure-login.example-account.top","public_suffix":"top","registrable_domain":"example-account.top"},"malware_detection":false,"reasons":["+15 unencrypted http scheme","+10 top-level domain frequently seen in abuse reports","+15 multiple credential-lure keywords"],"risk_level":"high","risk_score":55,"valid":true}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"URL Safety","tags":["Runs on Vercel"],"x-ozy-permission":"url","x-ozy-units":1}},"/api/v1/webhooks":{"post":{"description":"Creates a relay with a private receive URL and a signing secret. Senders must sign requests (timestamped HMAC-SHA256, or GitHub's x-hub-signature-256), so the endpoint cannot act as an open relay. Accepted events are de-duplicated, filtered by event-type patterns, re-signed and forwarded to your public HTTPS destination without following redirects. Failed deliveries retry with exponential backoff (6 attempts), every attempt is logged, and final failures create a notification.\n\nRuntime: Runs on Vercel. Runs on Vercel. Retries are driven by /api/v1/internal/tick.","operationId":"webhooks","parameters":[],"requestBody":{"content":{"application/json":{"example":{"destination":"https://hooks.example.com/ozy","event_types":["order.*"]},"schema":{"properties":{"destination":{"description":"Public https URL that receives forwarded events (string)"},"event_types":{"description":"Patterns such as \"order.*\" or \"push\" (default [\"*\"]) (string[])"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"event_types":["order.*"],"id":"3c55…","receive_url":"https://dev.itsozy.lol/api/v1/hooks/3c55…","secret":"whsec_…"}}},"description":"Success"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error"}},"security":[{"bearer":[]}],"summary":"Webhook Relay","tags":["Runs on Vercel"],"x-ozy-permission":"webhooks","x-ozy-units":1}}},"servers":[{"url":"https://dev.itsozy.lol"}]}