{"openapi":"3.1.0","x-discovery":{"ownershipProofs":["0xb6e82894c733f729cc45b68f9c62f06daeac48f4ce6b2211916ed0b275d5e8a155a65f98bf0d937be1fe8e88b7d27e34a1ca14101cb1976f916eb92046a26c951c"]},"info":{"title":"webcap","version":"0.1.0","description":"Pay-per-call web capture: any URL becomes a PNG/JPEG/PDF screenshot + free Open Graph metadata, or structured text/JSON via batch extract. Paid per call in USDC over x402 (HTTP 402, x402 v2 \"exact\" scheme, gasless EIP-3009 — the facilitator settles, no ETH or gas for the payer). No API keys, no accounts for x402 routes.","x-guidance":"Agent usage: paid x402 endpoints settle per call in USDC - POST /v1/x402/capture (0.001 USDC), POST /v1/x402/extract (0.01 USDC, batch up to 50 URLs for one payment), POST /v1/x402/audit (0.002 USDC, SEO basics + link/OG health in one call), POST /v1/x402/map-lite (0.002 USDC, sitemap/robots + 1-hop crawl URL list), POST /v1/x402/video (0.005 USDC, scroll-capture MP4/WebM), and POST /v1/x402/watches/topup (dynamic 0.1-1 USDC per 100-run watch pack). Payment (x402 v2 \"exact\"): on HTTP 402 read the base64 PAYMENT-REQUIRED header, sign the gasless EIP-3009 USDC transferWithAuthorization, and retry with the PAYMENT-SIGNATURE header. Every paid path also answers and accepts the challenge for GET, with the same parameters in the query string (sign the challenge for the method you then retry). No API keys or accounts. Free entry point: GET /v1/extract/preview samples the extract output without paying; full catalog at GET /openapi.json, agent skill at GET /skill.md.","contact":{"email":"jsummerill.ltd@gmail.com"}},"servers":[{"url":"https://webcap.shoutsid.fyi","description":"public deployment"}],"tags":[{"name":"capture","description":"URL → PNG/JPEG/PDF screenshot (+ free OG metadata)"},{"name":"extract","description":"URL(s) → structured text/JSON"},{"name":"audit","description":"URL → SEO basics + link/OG health report"},{"name":"map-lite","description":"Seed URL → same-host URL list (sitemap/robots + 1-hop crawl)"},{"name":"monitoring","description":"Scheduled watches: create, state, delete, x402 credit top-up"},{"name":"artifacts","description":"Stored capture artifacts"},{"name":"accounts","description":"API-key accounts, credit packs, and the credit-metered capture (non-x402 front door)"},{"name":"discovery","description":"Service metadata, catalog, icon, SEO surface"},{"name":"ml","description":"AI-powered visual analysis (classification, accessibility, entities, sentiment)"}],"paths":{"/v1/x402/capture":{"post":{"tags":["capture"],"summary":"Capture a URL as a screenshot (paid, x402)","description":"Capture the URL as a PNG/JPEG/PDF screenshot plus free OG metadata. Unpaid requests receive the x402 402 challenge; paying clients retry with PAYMENT-SIGNATURE. One payment per URL. Optional options tune the render: viewport {width, height}, deviceScaleFactor, isMobile, userAgent, proxy (\"auto\"/\"stealth\"/URL), waitFor {selector, timeoutMs}, and actions (click/type/wait, up to 5), plus timeoutMs/fullPage.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"The page to capture","example":"https://example.com/"},"format":{"type":"string","enum":["png","jpeg","pdf"],"description":"Screenshot format (default png)"},"options":{"type":"object","additionalProperties":false,"properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}}}}}}},"responses":{"200":{"description":"Paid + settled; the artifact (base64) and its canonical public URL","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"format":{"type":"string","enum":["png","jpeg","pdf"]},"bytes":{"type":"integer","example":204800},"data":{"type":"string","description":"Base64-encoded image bytes"},"url":{"type":"string","example":"{publicBaseUrl}/v1/artifacts/{id}","description":"Canonical public artifact URL"}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":1000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.001 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/capture"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"1000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url, format or options. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-payer spend cap exceeded (error envelope, code spend_cap_exceeded; detail {payer, spent, cap, reason}; caps come from WEBCAP_SPEND_CAP_USDC_UNITS, unset means unlimited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.001000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["capture"],"summary":"(GET form) Capture a URL as a screenshot (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","required":true,"description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","description":"The page to capture","example":"https://example.com/"}},{"name":"format","in":"query","description":"Same value as the POST body field `format`; encode arrays and objects as JSON.","schema":{"type":"string","enum":["png","jpeg","pdf"],"description":"Screenshot format (default png)"}},{"name":"options","in":"query","description":"Same value as the POST body field `options`; encode arrays and objects as JSON.","schema":{"type":"object","additionalProperties":false,"properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}}}],"responses":{"200":{"description":"Paid + settled; the artifact (base64) and its canonical public URL","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"format":{"type":"string","enum":["png","jpeg","pdf"]},"bytes":{"type":"integer","example":204800},"data":{"type":"string","description":"Base64-encoded image bytes"},"url":{"type":"string","example":"{publicBaseUrl}/v1/artifacts/{id}","description":"Canonical public artifact URL"}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":1000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.001 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/capture"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"1000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url, format or options. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-payer spend cap exceeded (error envelope, code spend_cap_exceeded; detail {payer, spent, cap, reason}; caps come from WEBCAP_SPEND_CAP_USDC_UNITS, unset means unlimited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.001000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/extract":{"post":{"tags":["extract"],"summary":"Extract structured content from one URL or a batch (paid, x402)","description":"Return structured content (title, headings, paragraphs, links, images, word count, markdown) as JSON, taken from the page main content: navigation, cookie banners, sidebars and footers are excluded from paragraphs/markdown, so an agent pays for the document rather than the chrome (content.source / content.words / content.truncated report exactly what was kept). Batch up to 50 URLs for ONE payment (0.01 USDC covers the whole batch; the price is flat per batch while compute cost scales per URL, so one payment covers repeat extractions of the same batch only when re-requested). Optional natural-language \"schema\" triggers model-based extraction into custom JSON. A JSON object \"schema\" instead takes the deterministic path: zero model calls, the response data gains an \"extracted\" projection, with optional \"spans\" grounding each {field, quote, page} as a verbatim markdown substring. Optional options tune the capture per URL: proxy, waitFor {selector, timeoutMs}, actions, viewport, and the other render fields.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","oneOf":[{"required":["url"]},{"required":["urls"]}],"properties":{"url":{"type":"string","format":"uri","description":"A single absolute http(s) page to extract","example":"https://example.com/"},"urls":{"type":"array","description":"Batch of absolute http(s) pages for one payment (at most 50)","items":{"type":"string","format":"uri"},"maxItems":50},"schema":{"type":"string","description":"Optional plain natural-language string describing the JSON to extract via a model (a string, not a JSON object). A JSON object schema instead takes the deterministic path: zero model calls, the response data gains an \"extracted\" projection of the page structure, and failures 422 with detail string[] ($-rooted schema errors, \"span not grounded\" grounding errors, or \"unsupported schema keyword\" for oneOf/anyOf/allOf/$ref/format)"},"spans":{"type":"array","description":"Optional grounding spans for the deterministic object-schema path: each {field, quote, page} must be a verbatim substring of the cited markdown page, else 422 (omitted or [] skips grounding)","items":{"type":"object","properties":{"field":{"type":"string","description":"Extracted field the quote grounds"},"quote":{"type":"string","description":"Verbatim substring of the cited page markdown"},"page":{"type":"integer","description":"Zero-based index into the batch markdown pages"}}}},"options":{"type":"object","additionalProperties":false,"description":"Capture options forwarded to the capture pipeline (proxy/waitFor/actions/viewport)","properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}}}}}}},"responses":{"200":{"description":"Paid + settled; per-URL results (ok/error)","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"status":{"type":"string","enum":["ok","error"]},"data":{"type":"object","description":"Present when status=ok; the structured content (plus optional model-extracted \"extracted\")","properties":{"title":{"type":"string"},"description":{"type":"string"},"headings":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"text":{"type":"string"}}}},"paragraphs":{"type":"array","items":{"type":"string"}},"links":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"text":{"type":"string"}}}},"images":{"type":"array","items":{"type":"object","properties":{"src":{"type":"string"},"alt":{"type":"string"}}}},"wordCount":{"type":"integer","description":"Words on the whole page, chrome included (the human count)"},"markdown":{"type":"string"},"content":{"type":"object","description":"Provenance of the main content that paragraphs/markdown actually contain: which container was chosen, how many words it holds after chrome removal, and whether a maxContentWords budget cut it short","properties":{"source":{"type":"string","example":"article","description":"Selector the content came from; \"body\" = whole-page fallback"},"words":{"type":"integer"},"truncated":{"type":"boolean"}}},"extracted":{"type":"object","description":"Model-extracted JSON when a schema was supplied"},"classification":{"type":"object","description":"Deterministic page classification (rule-based, no model): category + confidence + tags","properties":{"category":{"type":"string","description":"Page category (article, product, documentation, landing-page, forum, social-media, e-commerce, news, blog, wiki, dashboard, form, other)"},"confidence":{"type":"number","description":"0.4 (no signal) to 0.8 (og:type match)"},"tags":{"type":"array","items":{"type":"string"}},"mode":{"type":"string","example":"deterministic"}}}}},"error":{"type":"string","description":"Present when status=error"}}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":10000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.01 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/extract"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"10000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url(s), schema, or spans. Object-schema failures carry $-rooted detail string[] (schema mismatch, ungrounded spans, unsupported keywords oneOf/anyOf/allOf/$ref/format); SSRF-blocked hosts carry detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"All URLs in the batch failed to extract (error envelope, code extract_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["extract"],"summary":"(GET form) Extract structured content from one URL or a batch (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","format":"uri","description":"A single absolute http(s) page to extract","example":"https://example.com/"}},{"name":"urls","in":"query","description":"Same value as the POST body field `urls`; encode arrays and objects as JSON.","schema":{"type":"array","description":"Batch of absolute http(s) pages for one payment (at most 50)","items":{"type":"string","format":"uri"},"maxItems":50}},{"name":"schema","in":"query","description":"Same value as the POST body field `schema`; encode arrays and objects as JSON.","schema":{"type":"string","description":"Optional plain natural-language string describing the JSON to extract via a model (a string, not a JSON object). A JSON object schema instead takes the deterministic path: zero model calls, the response data gains an \"extracted\" projection of the page structure, and failures 422 with detail string[] ($-rooted schema errors, \"span not grounded\" grounding errors, or \"unsupported schema keyword\" for oneOf/anyOf/allOf/$ref/format)"}},{"name":"spans","in":"query","description":"Same value as the POST body field `spans`; encode arrays and objects as JSON.","schema":{"type":"array","description":"Optional grounding spans for the deterministic object-schema path: each {field, quote, page} must be a verbatim substring of the cited markdown page, else 422 (omitted or [] skips grounding)","items":{"type":"object","properties":{"field":{"type":"string","description":"Extracted field the quote grounds"},"quote":{"type":"string","description":"Verbatim substring of the cited page markdown"},"page":{"type":"integer","description":"Zero-based index into the batch markdown pages"}}}}},{"name":"options","in":"query","description":"Same value as the POST body field `options`; encode arrays and objects as JSON.","schema":{"type":"object","additionalProperties":false,"description":"Capture options forwarded to the capture pipeline (proxy/waitFor/actions/viewport)","properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}}}],"responses":{"200":{"description":"Paid + settled; per-URL results (ok/error)","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"status":{"type":"string","enum":["ok","error"]},"data":{"type":"object","description":"Present when status=ok; the structured content (plus optional model-extracted \"extracted\")","properties":{"title":{"type":"string"},"description":{"type":"string"},"headings":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"text":{"type":"string"}}}},"paragraphs":{"type":"array","items":{"type":"string"}},"links":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"text":{"type":"string"}}}},"images":{"type":"array","items":{"type":"object","properties":{"src":{"type":"string"},"alt":{"type":"string"}}}},"wordCount":{"type":"integer","description":"Words on the whole page, chrome included (the human count)"},"markdown":{"type":"string"},"content":{"type":"object","description":"Provenance of the main content that paragraphs/markdown actually contain: which container was chosen, how many words it holds after chrome removal, and whether a maxContentWords budget cut it short","properties":{"source":{"type":"string","example":"article","description":"Selector the content came from; \"body\" = whole-page fallback"},"words":{"type":"integer"},"truncated":{"type":"boolean"}}},"extracted":{"type":"object","description":"Model-extracted JSON when a schema was supplied"},"classification":{"type":"object","description":"Deterministic page classification (rule-based, no model): category + confidence + tags","properties":{"category":{"type":"string","description":"Page category (article, product, documentation, landing-page, forum, social-media, e-commerce, news, blog, wiki, dashboard, form, other)"},"confidence":{"type":"number","description":"0.4 (no signal) to 0.8 (og:type match)"},"tags":{"type":"array","items":{"type":"string"}},"mode":{"type":"string","example":"deterministic"}}}}},"error":{"type":"string","description":"Present when status=error"}}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":10000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.01 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/extract"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"10000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url(s), schema, or spans. Object-schema failures carry $-rooted detail string[] (schema mismatch, ungrounded spans, unsupported keywords oneOf/anyOf/allOf/$ref/format); SSRF-blocked hosts carry detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"All URLs in the batch failed to extract (error envelope, code extract_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/audit":{"post":{"tags":["audit"],"summary":"Audit a URL for SEO, OG tags, and link health (paid, x402)","description":"Audit one URL for SEO signals, Open Graph presence, and link health via a single captureStructured call. Unpaid requests receive the x402 402 challenge; paying clients retry with PAYMENT-SIGNATURE. One payment per URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","example":"https://example.com/"}}}}}},"responses":{"200":{"description":"Paid + settled; the audit report","content":{"application/json":{"schema":{"type":"object","properties":{"audit":{"type":"object","properties":{"url":{"type":"string"},"seo":{"type":"object","properties":{"title":{"type":"object","properties":{"present":{"type":"boolean"},"length":{"type":"integer"},"ok":{"type":"boolean"}}},"description":{"type":"object","properties":{"present":{"type":"boolean"},"length":{"type":"integer"},"ok":{"type":"boolean"}}},"h1Count":{"type":"integer"},"h1Ok":{"type":"boolean"},"canonical":{"type":"object","properties":{"present":{"type":"boolean"},"value":{"type":"string"}}},"robotsMeta":{"type":"object","properties":{"present":{"type":"boolean"},"value":{"type":"string"}}}}},"og":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"image":{"type":"string"},"twitterCard":{"type":"string"},"twitterSite":{"type":"string"},"twitterCreator":{"type":"string"},"twitterTitle":{"type":"string"},"twitterDescription":{"type":"string"},"twitterImage":{"type":"string"},"articlePublishedTime":{"type":"string"},"articleAuthor":{"type":"string"},"articleSection":{"type":"string"},"articleTags":{"type":"array","items":{"type":"string"}},"present":{"type":"object","properties":{"title":{"type":"boolean"},"description":{"type":"boolean"},"image":{"type":"boolean"},"twitterCard":{"type":"boolean"},"twitterSite":{"type":"boolean"},"twitterCreator":{"type":"boolean"},"twitterTitle":{"type":"boolean"},"twitterDescription":{"type":"boolean"},"twitterImage":{"type":"boolean"},"articlePublishedTime":{"type":"boolean"},"articleAuthor":{"type":"boolean"},"articleSection":{"type":"boolean"},"articleTags":{"type":"boolean"}}}}},"links":{"type":"object","properties":{"total":{"type":"integer"},"internal":{"type":"integer"},"external":{"type":"integer"},"emptyText":{"type":"integer"},"duplicates":{"type":"integer"},"sample":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"text":{"type":"string"}}}}}}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":2000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.002 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/audit"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"2000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Audit failed for the URL (error envelope, code audit_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.002000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["audit"],"summary":"(GET form) Audit a URL for SEO, OG tags, and link health (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","required":true,"description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","format":"uri","example":"https://example.com/"}}],"responses":{"200":{"description":"Paid + settled; the audit report","content":{"application/json":{"schema":{"type":"object","properties":{"audit":{"type":"object","properties":{"url":{"type":"string"},"seo":{"type":"object","properties":{"title":{"type":"object","properties":{"present":{"type":"boolean"},"length":{"type":"integer"},"ok":{"type":"boolean"}}},"description":{"type":"object","properties":{"present":{"type":"boolean"},"length":{"type":"integer"},"ok":{"type":"boolean"}}},"h1Count":{"type":"integer"},"h1Ok":{"type":"boolean"},"canonical":{"type":"object","properties":{"present":{"type":"boolean"},"value":{"type":"string"}}},"robotsMeta":{"type":"object","properties":{"present":{"type":"boolean"},"value":{"type":"string"}}}}},"og":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"image":{"type":"string"},"twitterCard":{"type":"string"},"twitterSite":{"type":"string"},"twitterCreator":{"type":"string"},"twitterTitle":{"type":"string"},"twitterDescription":{"type":"string"},"twitterImage":{"type":"string"},"articlePublishedTime":{"type":"string"},"articleAuthor":{"type":"string"},"articleSection":{"type":"string"},"articleTags":{"type":"array","items":{"type":"string"}},"present":{"type":"object","properties":{"title":{"type":"boolean"},"description":{"type":"boolean"},"image":{"type":"boolean"},"twitterCard":{"type":"boolean"},"twitterSite":{"type":"boolean"},"twitterCreator":{"type":"boolean"},"twitterTitle":{"type":"boolean"},"twitterDescription":{"type":"boolean"},"twitterImage":{"type":"boolean"},"articlePublishedTime":{"type":"boolean"},"articleAuthor":{"type":"boolean"},"articleSection":{"type":"boolean"},"articleTags":{"type":"boolean"}}}}},"links":{"type":"object","properties":{"total":{"type":"integer"},"internal":{"type":"integer"},"external":{"type":"integer"},"emptyText":{"type":"integer"},"duplicates":{"type":"integer"},"sample":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"text":{"type":"string"}}}}}}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":2000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.002 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/audit"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"2000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Audit failed for the URL (error envelope, code audit_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.002000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/map-lite":{"post":{"tags":["map-lite"],"summary":"Map a site to its URL list via sitemap/robots + 1-hop crawl (paid, x402)","description":"Return the same-host URL list for a seed page: sitemap.xml (via robots.txt Sitemap lines, falling back to /sitemap.xml, following sitemap indexes) plus a 1-hop same-host link crawl when no sitemap yields URLs. Unpaid requests receive the x402 402 challenge; paying clients retry with PAYMENT-SIGNATURE. One payment per seed (audit tier).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"The seed page to map","example":"https://example.com/"},"maxUrls":{"type":"integer","minimum":1,"maximum":50,"default":20,"description":"Maximum URLs to return (default 20, at most 50)"}}}}}},"responses":{"200":{"description":"Paid + settled; the discovered URL list (empty when none found)","content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"string"},"description":"Same-host URLs discovered via sitemap/robots plus a 1-hop crawl (empty when none found)"},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":2000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.002 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/map-lite"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"2000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url or maxUrls. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Map-lite discovery failed for the URL (error envelope, code map_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.002000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["map-lite"],"summary":"(GET form) Map a site to its URL list via sitemap/robots + 1-hop crawl (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","required":true,"description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","format":"uri","description":"The seed page to map","example":"https://example.com/"}},{"name":"maxUrls","in":"query","description":"Same value as the POST body field `maxUrls`; encode arrays and objects as JSON.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20,"description":"Maximum URLs to return (default 20, at most 50)"}}],"responses":{"200":{"description":"Paid + settled; the discovered URL list (empty when none found)","content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"string"},"description":"Same-host URLs discovered via sitemap/robots plus a 1-hop crawl (empty when none found)"},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":2000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.002 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/map-lite"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"2000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url or maxUrls. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Map-lite discovery failed for the URL (error envelope, code map_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.002000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/video":{"post":{"tags":["capture"],"summary":"Scroll-capture a URL as an MP4/WebM video (paid, x402)","description":"Record a deadline-bounded scroll choreography of the page to an in-memory MP4/WebM video. Unpaid requests receive the x402 402 challenge; paying clients retry with PAYMENT-SIGNATURE. One payment per URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"The page to scroll-capture","example":"https://example.com/"},"format":{"type":"string","enum":["mp4","webm"],"description":"Video format (default mp4)"},"durationMs":{"type":"integer","description":"Recording duration in milliseconds (default 5000, at most 30000)"},"scrollSpeed":{"type":"integer","description":"Pixels scrolled per choreography step (default 800, at most 5000)"},"scrollEasing":{"type":"string","enum":["linear","ease-in-out"],"description":"Scroll easing (default linear)"},"options":{"type":"object","additionalProperties":false,"description":"Capture options (viewport forwarded to the recording context)","properties":{"viewport":{"type":"object","description":"Recording viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}}}}}}}}},"responses":{"200":{"description":"Paid + settled; the video artifact (base64)","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"mime":{"type":"string","enum":["video/mp4","video/webm"]},"bytes":{"type":"integer","example":1048576},"data":{"type":"string","description":"Base64-encoded video bytes"}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":5000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.005 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/video"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"5000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url, format, durationMs, scrollSpeed or scrollEasing. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Video capture failed for the URL (error envelope, code video_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.005000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["capture"],"summary":"(GET form) Scroll-capture a URL as an MP4/WebM video (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","required":true,"description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","format":"uri","description":"The page to scroll-capture","example":"https://example.com/"}},{"name":"format","in":"query","description":"Same value as the POST body field `format`; encode arrays and objects as JSON.","schema":{"type":"string","enum":["mp4","webm"],"description":"Video format (default mp4)"}},{"name":"durationMs","in":"query","description":"Same value as the POST body field `durationMs`; encode arrays and objects as JSON.","schema":{"type":"integer","description":"Recording duration in milliseconds (default 5000, at most 30000)"}},{"name":"scrollSpeed","in":"query","description":"Same value as the POST body field `scrollSpeed`; encode arrays and objects as JSON.","schema":{"type":"integer","description":"Pixels scrolled per choreography step (default 800, at most 5000)"}},{"name":"scrollEasing","in":"query","description":"Same value as the POST body field `scrollEasing`; encode arrays and objects as JSON.","schema":{"type":"string","enum":["linear","ease-in-out"],"description":"Scroll easing (default linear)"}},{"name":"options","in":"query","description":"Same value as the POST body field `options`; encode arrays and objects as JSON.","schema":{"type":"object","additionalProperties":false,"description":"Capture options (viewport forwarded to the recording context)","properties":{"viewport":{"type":"object","description":"Recording viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}}}}}],"responses":{"200":{"description":"Paid + settled; the video artifact (base64)","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"mime":{"type":"string","enum":["video/mp4","video/webm"]},"bytes":{"type":"integer","example":1048576},"data":{"type":"string","description":"Base64-encoded video bytes"}}},"payment":{"type":"object","properties":{"payer":{"type":"string","example":"0x000000000000000000000000000000000000dEaD"},"creditsUsed":{"type":"integer","description":"Credits charged (credits rail and async jobs; omitted on x402-settled calls)"},"costUsdcUnits":{"type":"integer","description":"Amortized compute cost in atomic 6-decimal USDC units"},"priceUsdcUnits":{"type":"integer","example":5000}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is 0.005 USDC. Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/video"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"5000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"422":{"description":"Invalid input: missing/invalid url, format, durationMs, scrollSpeed or scrollEasing. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Video capture failed for the URL (error envelope, code video_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.005000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/watches":{"post":{"tags":["monitoring"],"summary":"Create a scheduled watch (free; pre-paid runs via the x402 top-up)","description":"Register an https URL + interval; webcap re-runs the capture or extract pipeline on schedule, detects changes, and fires the webhook when one is set. The watch starts with 0 credits: its first due run is recorded as no-credit and pauses it until a top-up. The first run after creation is due immediately (next scheduler tick).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","every","mode"],"properties":{"url":{"type":"string","description":"The https URL to watch","example":"https://example.com/"},"every":{"type":"string","enum":["15m","1h","6h","24h"],"description":"Run interval"},"mode":{"type":"string","enum":["capture","extract"],"description":"Re-run the capture (screenshot) or the extract (structured content) pipeline"},"schema":{"type":"string","description":"Optional natural-language extraction schema (extract mode)"},"webhook":{"type":"string","description":"Optional https URL that receives a change alert when a run detects a change"}}}}}},"responses":{"201":{"description":"Created; {id, state} with the full watch state (no runs yet)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"every":{"type":"string"},"mode":{"type":"string"},"schema":{"type":"string","description":"Present when set"},"webhook":{"type":"string","description":"Present when set"},"credits":{"type":"integer","description":"Pre-paid runs remaining (each run consumes 1)"},"paused":{"type":"boolean","description":"True when the watch ran out of credits and waits for a top-up"},"nextRunAt":{"type":["string","null"]},"lastRunAt":{"type":["string","null"]},"runs":{"type":"array","description":"The last ~10 runs, newest first","items":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["ok","error","no-credit"]},"changed":{"type":"boolean"},"artifactUrl":{"type":"string","description":"Present for ok capture runs"},"extract":{"type":"object","description":"Present for ok extract runs; the extract JSON of that run"},"diffSummary":{"type":"string","description":"Present when changed; compact list of changed paths (capture: \"artifact\")"},"webhook":{"type":"string","description":"Present when a change alert fired; delivery outcome (e.g. \"ok: HTTP 200\")"},"error":{"type":"string","description":"Present when status=error"},"createdAt":{"type":"string"}}}}}}}}}}},"400":{"description":"Invalid watch spec: url, every, mode, schema or webhook (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/watches/{id}":{"get":{"tags":["monitoring"],"summary":"Watch state + the last ~10 runs (newest first)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The watch state incl. credits, paused, next/last run and recent runs","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"every":{"type":"string"},"mode":{"type":"string"},"schema":{"type":"string","description":"Present when set"},"webhook":{"type":"string","description":"Present when set"},"credits":{"type":"integer","description":"Pre-paid runs remaining (each run consumes 1)"},"paused":{"type":"boolean","description":"True when the watch ran out of credits and waits for a top-up"},"nextRunAt":{"type":["string","null"]},"lastRunAt":{"type":["string","null"]},"runs":{"type":"array","description":"The last ~10 runs, newest first","items":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["ok","error","no-credit"]},"changed":{"type":"boolean"},"artifactUrl":{"type":"string","description":"Present for ok capture runs"},"extract":{"type":"object","description":"Present for ok extract runs; the extract JSON of that run"},"diffSummary":{"type":"string","description":"Present when changed; compact list of changed paths (capture: \"artifact\")"},"webhook":{"type":"string","description":"Present when a change alert fired; delivery outcome (e.g. \"ok: HTTP 200\")"},"error":{"type":"string","description":"Present when status=error"},"createdAt":{"type":"string"}}}}}}}}},"404":{"description":"Unknown watch id (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]},"delete":{"tags":["monitoring"],"summary":"Delete a watch and all of its runs","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted; empty body"},"404":{"description":"Unknown watch id (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/watches/topup":{"post":{"tags":["monitoring"],"summary":"Top up a watch with a 100-run pack (paid, x402)","description":"Buy a 100-run pack for a watch, priced at the watch's mode unit price × 100: capture 0.001 USDC × 100 = 0.1 USDC; extract 0.01 USDC × 100 = 1 USDC. On success credits += 100, a paused watch resumes and its next_run_at is rescheduled. Include the watch as ?watchId= in the URL as well as in the body so the 402 challenge can price the pack at the watch mode before payment.","parameters":[{"name":"watchId","in":"query","required":false,"schema":{"type":"string"},"description":"The watch to top up; when present, the 402 challenge amount is that watch mode unit price × 100 (falls back to the capture-mode pack price when unknown)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["watchId","runs"],"properties":{"watchId":{"type":"string","description":"The watch to top up"},"runs":{"type":"integer","enum":[100],"description":"Pack size in runs (always 100)"}}}}}},"responses":{"200":{"description":"Paid + settled; the new credit balance and the pack price","content":{"application/json":{"schema":{"type":"object","properties":{"watchId":{"type":"string"},"credits":{"type":"integer","description":"The watch credit balance after the top-up"},"priceUsdcUnits":{"type":"integer","description":"The pack price paid: the watch mode unit price × 100"}}}}}},"400":{"description":"Malformed JSON body, or missing/invalid watchId or runs (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is the watch mode unit price × 100 (capture 0.1 USDC, extract 1 USDC). Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/watches/topup"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"100000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"404":{"description":"Unknown watchId (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"dynamic","currency":"USD","min":"0.100000","max":"1.000000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["monitoring"],"summary":"(GET form) Top up a watch with a 100-run pack (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"watchId","in":"query","required":true,"description":"Same value as the POST body field `watchId`; encode arrays and objects as JSON.","schema":{"type":"string","description":"The watch to top up"}},{"name":"runs","in":"query","required":true,"description":"Same value as the POST body field `runs`; encode arrays and objects as JSON.","schema":{"type":"integer","enum":[100],"description":"Pack size in runs (always 100)"}}],"responses":{"200":{"description":"Paid + settled; the new credit balance and the pack price","content":{"application/json":{"schema":{"type":"object","properties":{"watchId":{"type":"string"},"credits":{"type":"integer","description":"The watch credit balance after the top-up"},"priceUsdcUnits":{"type":"integer","description":"The pack price paid: the watch mode unit price × 100"}}}}}},"400":{"description":"Malformed JSON body, or missing/invalid watchId or runs (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 v2). The challenge is sent as the base64-encoded JSON PAYMENT-REQUIRED response header AND as the equivalent JSON body shown below (curl/agent-friendly). The amount is the watch mode unit price × 100 (capture 0.1 USDC, extract 1 USDC). Sign accepts[0] (scheme \"exact\", gasless EIP-3009 transferWithAuthorization, USDC) and retry with the PAYMENT-SIGNATURE header; the facilitator verifies + settles on-chain. MPP clients charge the same accepts[0] terms through the mpp flow advertised in x-payment-info.","headers":{"PAYMENT-REQUIRED":{"description":"Base64-encoded JSON with the exact shape of the JSON body below (decode: base64 -d)","schema":{"type":"string"}},"WWW-Authenticate":{"description":"MPP Payment challenge (id, realm, method, intent, request) for the same price terms — see x-payment-info","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","description":"The x402 v2 payment challenge. Delivered TWICE per 402 response: base64-encoded in the PAYMENT-REQUIRED response header, and as this JSON body (curl/agent-friendly). Sign accepts[0] as a gasless EIP-3009 transferWithAuthorization and retry with the PAYMENT-SIGNATURE header.","properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","example":"https://webcap.shoutsid.fyi/v1/x402/watches/topup"},"description":{"type":"string"},"mimeType":{"type":"string","example":"application/json"},"serviceName":{"type":"string","example":"Webcap"},"tags":{"type":"array","items":{"type":"string"},"example":["screenshot","web-capture","pdf","markdown","text-extraction"]},"iconUrl":{"type":"string","example":"https://webcap.shoutsid.fyi/icon.png"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["exact"],"description":"x402 v2 scheme"},"network":{"type":"string","example":"eip155:8453","description":"CAIP-2 network id"},"asset":{"type":"string","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","description":"ERC-20 USDC contract address"},"amount":{"type":"string","example":"100000","description":"Price in atomic 6-decimal USDC units"},"payTo":{"type":"string","example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e","description":"Merchant wallet receiving the USDC"},"maxTimeoutSeconds":{"type":"integer","example":300},"extra":{"type":"object","description":"EIP-712 domain of the chain USDC deploy (needed to sign)","example":{"name":"USDC","version":"2"}}}}},"extensions":{"type":"object","properties":{"bazaar":{"type":"object","description":"CDP Bazaar discovery extension: service metadata, example input/output, and the full request schema (info, input, inputSchema, output, schema).","properties":{"info":{"type":"object","description":"Resource info incl. pinned input method (POST)"},"input":{"type":"object","description":"Example request body"},"inputSchema":{"type":"object","description":"JSON Schema of the request body"},"output":{"type":"object","description":"Example response"},"schema":{"type":"object","description":"Full request schema (method, input, output)"}}}}}}}}}},"404":{"description":"Unknown watchId (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"dynamic","currency":"USD","min":"0.100000","max":"1.000000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/analyze":{"post":{"tags":["ml"],"summary":"AI-powered visual analysis of a web page (paid, x402)","description":"Capture a screenshot and analyze it with AI. Supports classification, accessibility audit, layout analysis, entity extraction, and sentiment analysis. Uses a configured vision model when MODEL_API_BASE_URL/MODEL_API_KEY/MODEL_NAME are set; otherwise falls back to a deterministic DOM-based analysis (no model required).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","task"],"properties":{"url":{"type":"string","format":"uri","description":"URL to analyze"},"task":{"type":"string","enum":["classification","accessibility","layout","entities","sentiment"],"description":"Analysis task type"},"context":{"type":"string","description":"Optional context for the analysis"}}}}}},"responses":{"200":{"description":"Analysis result (result carries mode: \"model\" for vision-model output, \"deterministic\" for the no-model DOM fallback)","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"string","description":"Analysis task performed"},"result":{"type":"object","description":"Task-specific result"},"payment":{"type":"object","properties":{"payer":{"type":"string"},"priceUsdcUnits":{"type":"number"},"costUsdcUnits":{"type":"number"}}},"latency_ms":{"type":"number","description":"Analysis latency in milliseconds"}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 challenge)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["ml"],"summary":"(GET form) AI-powered visual analysis of a web page (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"url","in":"query","required":true,"description":"Same value as the POST body field `url`; encode arrays and objects as JSON.","schema":{"type":"string","format":"uri","description":"URL to analyze"}},{"name":"task","in":"query","required":true,"description":"Same value as the POST body field `task`; encode arrays and objects as JSON.","schema":{"type":"string","enum":["classification","accessibility","layout","entities","sentiment"],"description":"Analysis task type"}},{"name":"context","in":"query","description":"Same value as the POST body field `context`; encode arrays and objects as JSON.","schema":{"type":"string","description":"Optional context for the analysis"}}],"responses":{"200":{"description":"Analysis result (result carries mode: \"model\" for vision-model output, \"deterministic\" for the no-model DOM fallback)","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"string","description":"Analysis task performed"},"result":{"type":"object","description":"Task-specific result"},"payment":{"type":"object","properties":{"payer":{"type":"string"},"priceUsdcUnits":{"type":"number"},"costUsdcUnits":{"type":"number"}}},"latency_ms":{"type":"number","description":"Analysis latency in milliseconds"}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 challenge)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/x402/analyze/batch":{"post":{"tags":["ml"],"summary":"Batch AI-powered visual analysis (paid, x402)","description":"Analyze multiple URLs with the same AI task. One payment covers the entire batch (up to 10 URLs).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["urls","task"],"properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10,"description":"URLs to analyze (max 10)"},"task":{"type":"string","enum":["classification","accessibility","layout","entities","sentiment"],"description":"Analysis task type"},"context":{"type":"string","description":"Optional context for the analysis"}}}}}},"responses":{"200":{"description":"Batch analysis results (each ok result carries mode: \"model\" or \"deterministic\")","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"status":{"type":"string","enum":["ok","error"]},"result":{"type":"object"},"error":{"type":"string"}}}},"task":{"type":"string"},"payment":{"type":"object","properties":{"payer":{"type":"string"},"priceUsdcUnits":{"type":"number"},"costUsdcUnits":{"type":"number"}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 challenge)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}},"get":{"tags":["ml"],"summary":"(GET form) Batch AI-powered visual analysis (paid, x402)","description":"GET form of this paid route: identical work, price and responses, with the POST body fields passed as query parameters instead. Numbers and booleans are typed on the wire (?maxUrls=5, ?fullPage=true) and arrays or objects are JSON-encoded (?urls=[\"https://…\"], ?options={\"maxContentWords\":800}). The 402 challenge is advertised per method, so sign the challenge you received for this method and retry with it.","parameters":[{"name":"urls","in":"query","required":true,"description":"Same value as the POST body field `urls`; encode arrays and objects as JSON.","schema":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10,"description":"URLs to analyze (max 10)"}},{"name":"task","in":"query","required":true,"description":"Same value as the POST body field `task`; encode arrays and objects as JSON.","schema":{"type":"string","enum":["classification","accessibility","layout","entities","sentiment"],"description":"Analysis task type"}},{"name":"context","in":"query","description":"Same value as the POST body field `context`; encode arrays and objects as JSON.","schema":{"type":"string","description":"Optional context for the analysis"}}],"responses":{"200":{"description":"Batch analysis results (each ok result carries mode: \"model\" or \"deterministic\")","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"status":{"type":"string","enum":["ok","error"]},"result":{"type":"object"},"error":{"type":"string"}}}},"task":{"type":"string"},"payment":{"type":"object","properties":{"payer":{"type":"string"},"priceUsdcUnits":{"type":"number"},"costUsdcUnits":{"type":"number"}}}}}}}},"400":{"description":"Malformed JSON request body (error envelope, code bad_request)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 challenge)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.010000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/extract/preview":{"get":{"tags":["extract"],"summary":"Free bounded structured preview (no payment, rate-limited)","description":"Sample the extract output without paying: a truncated preview (headings, links, first 1500 chars of markdown from the page's main content, chrome excluded). Rate-limited per client.","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string"},"description":"The page to preview"}],"responses":{"200":{"description":"Truncated structured preview + upgrade pointer to the paid endpoint","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"preview":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"headings":{"type":"array","items":{"type":"object","properties":{"level":{"type":"integer"},"text":{"type":"string"}}}},"links":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"text":{"type":"string"}}}},"wordCount":{"type":"integer","description":"Words on the whole page, chrome included"},"markdown":{"type":"string","description":"First 1500 characters of the document-order markdown, taken from the page's main content (chrome excluded)"},"content":{"type":"object","description":"What the markdown actually contains: source = the container it was taken from (article/main/body), words = words kept after chrome removal, truncated = cut short by the budget","properties":{"source":{"type":"string","example":"article"},"words":{"type":"integer"},"truncated":{"type":"boolean"}}}}},"truncated":{"type":"boolean","example":true},"upgrade":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/extract"},"note":{"type":"string"}}},"trial":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/trial"},"note":{"type":"string","description":"Free trial pointer: one full PNG capture per wallet (EIP-191 personal_sign proof)"}}},"paidUpgrade":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/extract","description":"The paid extract endpoint (method + path) that returns the full result"},"priceUsdc":{"type":"number","example":0.01,"description":"Per-extract price in USDC (priceUsdcUnits / 1e6), config-derived"},"priceUsdcUnits":{"type":"integer","example":10000,"description":"Per-extract price in atomic 6-decimal USDC units (config x402ExtractPriceUsdcUnits)"},"howToPay":{"type":"string","description":"The x402 v2 \"exact\" scheme payment flow: 402 challenge -> sign a gasless EIP-3009 USDC transferWithAuthorization -> retry with the PAYMENT-SIGNATURE header"},"guide":{"type":"string","description":"The deployment agent skill guide URL (<public base URL>/skill.md), config-derived"}}}}}}}},"422":{"description":"Missing or invalid url query parameter (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Preview rate limit exceeded (error envelope, code rate_limited; detail.retryAfterSeconds + detail.paidUpgrade{endpoint, priceUsdc, priceUsdcUnits, howToPay, guide} point at the paid extract endpoint)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial":{"post":{"tags":["capture"],"summary":"Free trial capture: one full PNG per wallet (EIP-191 proof, no payment)","description":"One free full-page PNG capture per wallet, proven by EIP-191 personal_sign of exactly \"Claim one free webcap trial capture for <payer>\" (<payer> = the lowercase 0x address). A wallet that already claimed gets 409 already_claimed. Rate-limited per client; the 200 and 409 responses carry a paidNext pointer at the paid capture endpoint. Never touches the revenue ledger. Free, no payment.","responses":{"200":{"description":"Trial artifact + trial receipt + paidNext pointer","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"format":{"type":"string","example":"png"},"bytes":{"type":"integer"},"data":{"type":"string","description":"Base64 PNG bytes"},"url":{"type":"string","description":"Persistent public artifact URL"}}},"trial":{"type":"object","properties":{"payer":{"type":"string"},"endpoint":{"type":"string","example":"capture"},"priceUsdcUnits":{"type":"integer","example":0}}},"paidNext":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/capture"},"priceUsdcUnits":{"type":"integer"},"guide":{"type":"string"}}},"remaining":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Trial signature invalid (malformed EIP-191 signature, or does not recover to payer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Wallet already claimed its trial (code already_claimed; detail.paidNext points at the paid capture endpoint)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing/invalid url, payer (must be a 0x EVM address), or signature (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Trial rate limit exceeded (error envelope, code rate_limited; detail.paidNext points at the paid capture endpoint)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/extract":{"post":{"tags":["capture"],"summary":"Free trial extract: one deterministic single-URL extraction per wallet","description":"Single URL only (urls/schema/model in the body answer 422 — batch + model live on the paid extract). Signature = EIP-191 personal_sign of exactly \"Claim one free webcap trial extract for <payer>\". Repeat claims 409 with paidNext + remaining. Never touches the revenue ledger. Free, no payment.","responses":{"200":{"description":"Trial results + trial receipt + paidNext pointer","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object"}},"trial":{"type":"object","properties":{"payer":{"type":"string"},"endpoint":{"type":"string","example":"extract"},"priceUsdcUnits":{"type":"integer","example":0}}},"paidNext":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/extract"},"priceUsdcUnits":{"type":"integer"},"guide":{"type":"string"}}},"remaining":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Trial signature invalid (malformed EIP-191 signature, or does not recover to payer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Wallet already claimed its extract trial (code already_claimed; detail carries paidNext + remaining)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing/invalid url, payer, or signature — or a paid-only field (urls/schema/model) was sent (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Trial rate limit exceeded (error envelope, code rate_limited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/audit":{"post":{"tags":["capture"],"summary":"Free trial audit: one SEO + link/OG health audit per wallet","description":"Full single-URL audit (title, description, OG tags, link health). Signature = EIP-191 personal_sign of exactly \"Claim one free webcap trial audit for <payer>\". Repeat claims 409 with paidNext + remaining. Never touches the revenue ledger. Free, no payment.","responses":{"200":{"description":"Trial audit + trial receipt + paidNext pointer","content":{"application/json":{"schema":{"type":"object","properties":{"audit":{"type":"object"},"trial":{"type":"object","properties":{"payer":{"type":"string"},"endpoint":{"type":"string","example":"audit"},"priceUsdcUnits":{"type":"integer","example":0}}},"paidNext":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/audit"},"priceUsdcUnits":{"type":"integer"},"guide":{"type":"string"}}},"remaining":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Trial signature invalid (malformed EIP-191 signature, or does not recover to payer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Wallet already claimed its audit trial (code already_claimed; detail carries paidNext + remaining)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing/invalid url, payer (must be a 0x EVM address), or signature (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Trial rate limit exceeded (error envelope, code rate_limited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/map-lite":{"post":{"tags":["capture"],"summary":"Free trial map-lite: one site map (capped at 10 URLs) per wallet","description":"Sitemap/robots + 1-hop same-host crawl, capped at 10 URLs (paid goes to 50). Signature = EIP-191 personal_sign of exactly \"Claim one free webcap trial map-lite for <payer>\". Repeat claims 409 with paidNext + remaining. Never touches the revenue ledger. Free, no payment.","responses":{"200":{"description":"Trial URL list + trial receipt + paidNext pointer","content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"string"}},"trial":{"type":"object","properties":{"payer":{"type":"string"},"endpoint":{"type":"string","example":"map-lite"},"priceUsdcUnits":{"type":"integer","example":0},"maxUrlsCap":{"type":"integer","example":10}}},"paidNext":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/map-lite"},"priceUsdcUnits":{"type":"integer"},"guide":{"type":"string"}}},"remaining":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Trial signature invalid (malformed EIP-191 signature, or does not recover to payer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Wallet already claimed its map-lite trial (code already_claimed; detail carries paidNext + remaining)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing/invalid url, payer (must be a 0x EVM address), signature, or maxUrls (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Trial rate limit exceeded (error envelope, code rate_limited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/analyze":{"post":{"tags":["capture"],"summary":"Free trial analyze: one deterministic single-URL analysis per wallet","description":"Deterministic analysis only — no model call even when the deployment has a model configured (model-backed analysis stays paid). Body {url, task, payer, signature}; task is one of classification|accessibility|layout|entities|sentiment. Signature = EIP-191 personal_sign of exactly \"Claim one free webcap trial analyze for <payer>\". Repeat claims 409 with paidNext + remaining. Never touches the revenue ledger. Free, no payment.","responses":{"200":{"description":"Trial analysis + trial receipt + paidNext pointer","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"type":"string","example":"classification"},"result":{"type":"object"},"trial":{"type":"object","properties":{"payer":{"type":"string"},"endpoint":{"type":"string","example":"analyze"},"priceUsdcUnits":{"type":"integer","example":0}}},"paidNext":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/analyze"},"priceUsdcUnits":{"type":"integer"},"guide":{"type":"string"}}},"remaining":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Trial signature invalid (malformed EIP-191 signature, or does not recover to payer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Wallet already claimed its analyze trial (code already_claimed; detail carries paidNext + remaining)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing/invalid url, task, payer (must be a 0x EVM address), or signature (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Trial rate limit exceeded (error envelope, code rate_limited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/status":{"get":{"tags":["discovery"],"summary":"Trial menu for a wallet: claimed/available trials, the claim recipe, and the paid catalog to move on to","description":"Free trials + the paid path in one call. `available` lists the trials this wallet can still claim (with the paid counterpart and price of each). `paid` is always the full priced catalog of paid endpoints, and `howToPay` is the x402 flow (scheme/network/asset/payTo mirror the 402 challenge), so a wallet that has used every trial still gets a concrete next call instead of an empty menu. `recurring` points at the watch path (create free, top up in 100-run packs). Free, no payment.","parameters":[{"name":"payer","in":"query","required":true,"schema":{"type":"string"},"description":"Lowercase 0x EVM address to look up"}],"responses":{"200":{"description":"Claimed + available trial endpoints with trial/paid paths and prices, plus the paid catalog, howToPay and recurring watch options","content":{"application/json":{"schema":{"type":"object","properties":{"payer":{"type":"string"},"claimed":{"type":"array","items":{"type":"string"}},"available":{"type":"array","items":{"type":"object"}},"allTrialsUsed":{"type":"boolean"},"nextStep":{"type":"string"},"paid":{"type":"array","description":"Every paid endpoint with its price; always present, even when no trials remain","items":{"type":"object","properties":{"endpoint":{"type":"string","example":"POST /v1/x402/capture"},"priceUsdcUnits":{"type":"integer"},"priceUsdc":{"type":"number"},"note":{"type":"string"}}}},"howToPay":{"type":"object","description":"The x402 payment flow, with the same scheme/network/asset/payTo the 402 challenge carries"},"recurring":{"type":"object","description":"Create a watch free, then top it up in 100-run packs"},"howToClaim":{"type":"object"}}}}}},"422":{"description":"payer query parameter must be a 0x EVM address (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/trial/quick":{"get":{"tags":["capture"],"summary":"No-wallet free JPEG thumbnail (3/day per IP)","description":"Zero-friction hook for bots that cannot sign: a JPEG capture thumbnail with no wallet and no signature, budgeted at 3 per IP per UTC day (429 faucet_exhausted past that, with a trial pointer). The full trials above are the product; this points at them. Free, no payment.","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string"},"description":"The page to thumbnail"}],"responses":{"200":{"description":"Thumbnail artifact + faucet budget state + trial pointer","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"format":{"type":"string","example":"jpeg"},"bytes":{"type":"integer"},"data":{"type":"string","description":"Base64 JPEG bytes"},"url":{"type":"string","description":"Persistent public artifact URL"}}},"faucet":{"type":"object"},"trial":{"type":"object"},"paidNext":{"type":"object"}}}}}},"422":{"description":"url query parameter is required (or the URL is blocked/malformed) (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Faucet budget spent for today (code faucet_exhausted; detail carries the wallet-trial pointer + paidNext)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/og-debugger":{"get":{"tags":["discovery"],"summary":"Free OG meta debugger tool page (no payment, rate-limited)","description":"Server-rendered link-preview debugger: omit url for the empty form, or pass ?url=… to see the Open Graph / meta tags the free GET /v1/og endpoint returns for that page — a preview card plus a tag table, or an inline error for bad/unreachable URLs. Plain GET form, no JavaScript. Fetch is rate-limited per client.","parameters":[{"name":"url","in":"query","required":false,"schema":{"type":"string"},"description":"The page to debug; omit for the empty form"}],"responses":{"200":{"description":"The debugger page (text/html; charset=utf-8)","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[]}},"/v1/artifacts/{id}":{"get":{"tags":["artifacts"],"summary":"Fetch a stored artifact (raw bytes, public)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The artifact bytes with its stored mime type (image/png, image/jpeg or application/pdf)","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"403":{"description":"Signed URL signature mismatch (error envelope, code forbidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Artifact not found (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Signed URL expired (error envelope, code gone)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/artifacts/{id}/page":{"get":{"tags":["artifacts"],"summary":"Shareable HTML page for an artifact (Open Graph tags, public)","description":"Renders the capture with og:title/og:description/og:type/og:url/og:image (og:url and og:image are the artifact public URL), the embedded image, the source URL, and format/size/captured-at metadata.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The artifact page (text/html; charset=utf-8)","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Artifact not found (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/":{"get":{"tags":["discovery"],"summary":"Product landing page (content-negotiated)","description":"Returns the product landing page (text/html; charset=utf-8). Clients that send Accept: application/json without text/html receive the JSON service map instead (endpoints, prices, payment status).","responses":{"200":{"description":"Landing page for HTML clients; JSON service map for pure-JSON clients","content":{"text/html":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","example":"webcap"},"tagline":{"type":"string"},"endpoints":{"type":"object","properties":{"free":{"type":"array"},"paid":{"type":"array"}}},"catalog":{"type":"string"},"agentGuide":{"type":"string"},"payment":{"type":"string"}}}}}}},"security":[]}},"/quickstart":{"get":{"tags":["discovery"],"summary":"Quick start guide — step-by-step walkthrough from free preview to paid API","description":"Server-rendered quick start guide that walks developers through the entire flow: free preview, x402 payment challenge, client library, wallet setup. Reduces conversion friction by providing copy-paste commands.","responses":{"200":{"description":"The quick start guide page (text/html; charset=utf-8)","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[]}},"/compare":{"get":{"tags":["discovery"],"summary":"Pricing comparison: webcap vs SaaS screenshot alternatives","description":"Side-by-side comparison of webcap pay-per-call pricing against Urlbox, ScreenshotAPI, Screenshotone, Browserless, and other screenshot API services. Shows per-call costs, payment methods, and account requirements.","responses":{"200":{"description":"text/html; charset=utf-8 — the comparison page","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[]}},"/buy":{"get":{"tags":["discovery"],"summary":"Buy credits page — credit pack purchase with card and crypto","description":"Server-rendered credit pack purchase page with three tiers (Starter $0.50, Pro $3, Max $12). Shows pricing, credit usage, and payment instructions for both Stripe card and x402 crypto payments.","responses":{"200":{"description":"The buy credits page (text/html; charset=utf-8)","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[]}},"/v1/stripe/checkout":{"post":{"tags":["discovery"],"summary":"Create a Stripe Checkout session for credit pack purchase","description":"Creates a Stripe Checkout session for buying credit packs (Starter $0.50, Pro $3, Max $12). When Stripe is not configured, returns a helpful error with alternative payment instructions (x402 crypto or email purchase).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pack"],"properties":{"pack":{"type":"string","enum":["starter","pro","max"],"description":"Credit pack to purchase"},"address":{"type":"string","description":"Optional Ethereum address to associate with the purchase"}}}}}},"responses":{"200":{"description":"Stripe Checkout session URL (when configured) or payment alternatives (when not configured)","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"checkoutUrl":{"type":"string","format":"uri","description":"URL to redirect to Stripe Checkout"}}},{"type":"object","properties":{"error":{"type":"string","example":"stripe_not_configured"},"message":{"type":"string"},"fallback":{"type":"object","properties":{"crypto":{"type":"string"},"email":{"type":"string"}}}}}]}}}},"422":{"description":"Missing or invalid pack field (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/demo":{"get":{"tags":["discovery"],"summary":"Sample full extract response (no payment, no rate limit)","description":"Returns a hardcoded example extract response so users can see the actual JSON output format before paying. Useful for understanding the API response structure.","responses":{"200":{"description":"Sample extract response with _demo flag","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","example":"https://example.com/"},"results":{"type":"array","items":{"type":"object"}},"_demo":{"type":"boolean","example":true},"_note":{"type":"string"}}}}}}},"security":[]}},"/icon.png":{"get":{"tags":["discovery"],"summary":"Service icon (PNG)","responses":{"200":{"description":"The webcap icon","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"500":{"description":"Icon file missing on the server (error envelope, code internal)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/openapi.json":{"get":{"tags":["discovery"],"summary":"This OpenAPI catalog","responses":{"200":{"description":"The OpenAPI 3.1 document","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenapiDocument"}}}}},"security":[]}},"/v1/status":{"get":{"tags":["discovery"],"summary":"Operational status dashboard (uptime, revenue totals, hit counts)","description":"Public, non-sensitive operational snapshot: server uptime, chain config, DB health, aggregate revenue totals (no payer-identifiable data), top endpoint hit counts, active watch count, and artifact count. Designed for status pages and monitoring.","responses":{"200":{"description":"Operational status snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"uptimeSeconds":{"type":"integer","description":"Seconds since server start"},"version":{"type":"string","example":"0.1.0"},"chain":{"type":"object","properties":{"id":{"type":"integer","example":8453},"name":{"type":"string","example":"base"},"network":{"type":"string","example":"eip155:8453"}}},"db":{"type":"object","properties":{"ok":{"type":"boolean"}}},"pricing":{"type":"object","properties":{"creditsPerUsdc":{"type":"integer"},"pricePerCredit":{"type":"number"}}},"revenue":{"type":"object","properties":{"totalRevenueUsdcUnits":{"type":"integer"},"totalCostUsdcUnits":{"type":"integer"},"netMarginUsdcUnits":{"type":"integer"},"requestCount":{"type":"integer"}}},"endpoints":{"type":"object","properties":{"topHits":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"hits":{"type":"integer"}}}}}},"watches":{"type":"object","properties":{"active":{"type":"integer"}}},"artifacts":{"type":"object","properties":{"count":{"type":"integer"}}}}}}}}},"security":[]}},"/v1/funnel":{"get":{"tags":["discovery"],"summary":"Conversion funnel analytics (last 24h)","description":"Structured conversion funnel with stage-by-stage conversion rates, hourly breakdown, referrer sources, error type breakdown, and waitlist count. Pass ?format=text for human-readable terminal output (no jq needed). Public, non-sensitive analytics for monitoring landing page performance.","parameters":[{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","text"]},"description":"Response format: json (default) or text for terminal-friendly output"}],"responses":{"200":{"description":"Funnel analytics data","content":{"application/json":{"schema":{"type":"object","properties":{"generatedAt":{"type":"string","format":"date-time"},"waitlistCount":{"type":"integer"},"funnel":{"type":"object","additionalProperties":{"type":"integer"},"description":"Raw event counts by event name"},"funnelStructured":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string","example":"page_view"},"count":{"type":"integer"},"conversionFromPrev":{"type":["number","null"],"description":"Percentage from previous stage, null for first/error stages"}}}},"funnelHourly":{"type":"array","items":{"type":"object","properties":{"hour":{"type":"string","example":"2026-09-09T14:00:00Z"},"events":{"type":"object","additionalProperties":{"type":"integer"}}}}},"funnelReferrers":{"type":"array","items":{"type":"object","properties":{"referrer":{"type":"string","example":"hacker_news"},"count":{"type":"integer"}}}},"errorBreakdown":{"type":"array","items":{"type":"object","properties":{"errorType":{"type":"string","example":"capture_failed_502"},"count":{"type":"integer"},"sampleUrls":{"type":"array","items":{"type":"string"}}}}}}}}}}},"security":[]}},"/v1/agent-funnel":{"get":{"tags":["discovery"],"summary":"Agent-income funnel (reach → challenge → trial → paid → retention → recurring)","description":"The conversion funnel for the customer this service actually has: an autonomous agent. Tracks reach (discovery clients reading llms.txt/skill.md/.well-known/openapi/x402-service), challenge (402s on paid routes), trial (wallet-signed free claims), paid (settled calls + distinct paying wallets from the revenue ledger), retention (first-pay vs repeat wallets, lifetime) and recurring (funded, unpaused watches). Aggregate only — never a raw payer address. Pass ?format=text for terminal output. See docs/strategy/agent-first.md for the north-star definition. Public, no auth.","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":8760,"default":168},"description":"Window in hours (default 168 = 7 days)"},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","text"]},"description":"Response format: json (default) or text for terminal-friendly output"}],"responses":{"200":{"description":"Agent funnel counts (JSON, or text/plain when ?format=text)","content":{"application/json":{"schema":{"type":"object","properties":{"windowHours":{"type":"integer","example":168},"reach":{"type":"object","properties":{"discoveryRequests":{"type":"integer"},"topClients":{"type":"array","items":{"type":"object","properties":{"client":{"type":"string","example":"CarbonMonitor/0.1"},"requests":{"type":"integer"}}}}}},"challenge":{"type":"object","properties":{"total":{"type":"integer","description":"402 responses on paid routes"},"topEndpoints":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"count":{"type":"integer"}}}}}},"trial":{"type":"object","properties":{"claims":{"type":"integer"},"wallets":{"type":"integer"},"byEndpoint":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"count":{"type":"integer"}}}}}},"paid":{"type":"object","properties":{"calls":{"type":"integer"},"wallets":{"type":"integer"},"revenueUsdcUnits":{"type":"integer","description":"Atomic 6-decimal USDC units"}}},"retention":{"type":"object","properties":{"firstPayWallets":{"type":"integer","description":"Wallets with exactly one settled call (lifetime)"},"repeatWallets":{"type":"integer","description":"Wallets with more than one settled call (lifetime)"}}},"recurring":{"type":"object","properties":{"activePaidWatches":{"type":"integer","description":"Unpaused watches holding credits"}}}}}}}}},"security":[]}},"/v1/status-badge":{"get":{"tags":["discovery"],"summary":"Shields.io-compatible status badge endpoint","description":"Returns a shields.io-compatible JSON response for embedding a live API status badge in README files. Returns operational status based on DB health check.","responses":{"200":{"description":"Status badge data","content":{"application/json":{"schema":{"type":"object","properties":{"schemaVersion":{"type":"integer","example":1},"label":{"type":"string","example":"API Status"},"message":{"type":"string","example":"operational"},"color":{"type":"string","example":"brightgreen"}}}}}}},"security":[]}},"/v1/waitlist":{"post":{"tags":["discovery"],"summary":"Join the email waitlist","description":"Stores an email address for the waitlist/mailing list. No auth required; fire-and-forget from the landing page.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"Email address to add to the waitlist"}}}}}},"responses":{"200":{"description":"Email added to waitlist","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"message":{"type":"string","example":"Added to waitlist"}}}}}},"422":{"description":"Missing or invalid email field (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/track":{"post":{"tags":["discovery"],"summary":"Lightweight landing page event tracking (fire-and-forget)","description":"Records page views, preview form submissions, and other conversion events for analytics. No auth required; fire-and-forget from client-side JavaScript.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event"],"properties":{"event":{"type":"string","description":"Event name (e.g., landing_view, preview_submit)"},"meta":{"type":"object","description":"Optional event metadata"}}}}}},"responses":{"200":{"description":"Event recorded","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"event":{"type":"string"}}}}}},"422":{"description":"Missing or invalid event field (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/x402/service":{"get":{"tags":["discovery"],"summary":"Agent-discoverable x402 service descriptor","description":"Full descriptor of the paid endpoints, the x402 v2 \"exact\" scheme, the USDC asset, payTo, the facilitator and howToPay, plus the free endpoints. 503 when x402 is disabled on this deployment.","responses":{"200":{"description":"The x402 service descriptor (paid endpoints, price, howToPay, free endpoints)","content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","example":"webcap"},"paymentProtocol":{"type":"string","example":"x402"},"x402Version":{"type":"integer","example":2},"paidEndpoints":{"type":"array","items":{"type":"object"}},"price":{"type":"object","properties":{"asset":{"type":"string"},"network":{"type":"string"},"payTo":{"type":"string"},"scheme":{"type":"string","example":"exact"}}},"howToPay":{"type":"string"},"facilitator":{"type":"string"},"freeEndpoints":{"type":"array","items":{"type":"object"}}}}}}},"503":{"description":"x402 disabled on this deployment (WEBCAP_CHAIN=local)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/health":{"get":{"tags":["discovery"],"summary":"Liveness + chain identity + uptime","responses":{"200":{"description":"Liveness probe: ok (includes DB health check), uptime, chainId, creditsPerUsdc, pricePerCredit","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true,"description":"true when the DB is responsive"},"uptimeSeconds":{"type":"integer","example":3600,"description":"Seconds since server start"},"chainId":{"type":"integer","example":8453},"creditsPerUsdc":{"type":"integer","example":100},"pricePerCredit":{"type":"number","example":0.01}}}}}}},"security":[]}},"/robots.txt":{"get":{"tags":["discovery"],"summary":"Crawler directives (allows all; points to the sitemap)","responses":{"200":{"description":"text/plain; charset=utf-8 — User-agent: * / Allow: / / Sitemap: <base>/sitemap.xml","content":{"text/plain":{"schema":{"type":"string"}}}}},"security":[]}},"/sitemap.xml":{"get":{"tags":["discovery"],"summary":"XML sitemap of the stable public paths (https-absolute)","responses":{"200":{"description":"application/xml; charset=utf-8 — one <loc> per public path, protocol-absolute https","content":{"application/xml":{"schema":{"type":"string"}}}}},"security":[]}},"/.well-known/x402":{"get":{"tags":["discovery"],"summary":"x402 machine-discovery catalog (endpoints, USDC prices, payTo, facilitator)","description":"What to call and what it costs: the paid endpoints with their USDC prices (the watch top-up shows the capture-mode pack price as usdc and the extract-mode pack price as usdcMax; the exact pack price is quoted per watch via ?watchId=), the free endpoints, and links to this catalog and the sitemap. network/asset/payTo are null when x402 is disabled on this deployment.","responses":{"200":{"description":"The x402 discovery catalog","content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","example":"webcap"},"description":{"type":"string"},"network":{"type":["string","null"],"example":"eip155:8453"},"asset":{"type":["string","null"],"example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"payTo":{"type":["string","null"],"example":"0xB25572D7317eb98EBb39c45Da40eAAEA2A56c25e"},"facilitator":{"type":"string","example":"https://api.cdp.coinbase.com/platform/v2/x402"},"endpoints":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","example":"POST /v1/x402/capture"},"usdc":{"type":"number","example":0.001},"usdcMax":{"type":"number","description":"Top-up route only: the extract-mode pack price","example":1},"description":{"type":"string"}}}},"free":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"note":{"type":"string"}}}},"openapi":{"type":"string","description":"This catalog, https-absolute"},"sitemap":{"type":"string","description":"The sitemap, https-absolute"},"ownershipProofs":{"type":"array","items":{"type":"string"},"description":"EIP-191 personal_sign of the service origin, signed by the payTo key; x402scan verified-ownership"}}}}}}},"security":[]}},"/.well-known/agent-card.json":{"get":{"tags":["discovery"],"summary":"A2A v1.0 agent card with the x402 payments section","description":"One document for agent-card consumers: service capabilities, skills (free trials first), and the x402 payment parameters (network, asset, payTo, facilitator). network/asset/payTo are null when x402 is disabled.","responses":{"200":{"description":"The agent card (application/json; charset=utf-8)","content":{"application/json":{"schema":{"type":"object","properties":{"protocolVersion":{"type":"string","example":"1.0"},"name":{"type":"string","example":"webcap"},"description":{"type":"string"},"url":{"type":"string"},"supportedInterfaces":{"type":"array","items":{"type":"object"}},"iconUrl":{"type":"string"},"version":{"type":"string","example":"1.1.0"},"provider":{"type":"object"},"documentationUrl":{"type":"string"},"capabilities":{"type":"object","properties":{"streaming":{"type":"boolean"},"pushNotifications":{"type":"boolean"}}},"defaultInputModes":{"type":"array","items":{"type":"string"}},"defaultOutputModes":{"type":"array","items":{"type":"string"}},"securitySchemes":{"type":"object"},"skills":{"type":"array","items":{"type":"object"}}}}}}}},"security":[]}},"/.well-known/openai-tools.json":{"get":{"tags":["discovery"],"summary":"Copy-paste tool definitions for LLM agents (OpenAI functions shape)","description":"One tool per free surface (preview, OG, trial status, the five wallet-trial claims, no-wallet thumbnail), each naming the exact HTTPS endpoint it maps to. Free, no payment.","responses":{"200":{"description":"The tool manifest (application/json; charset=utf-8)","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/.well-known/mcp-tools.json":{"get":{"tags":["discovery"],"summary":"Tool-router manifest (MCP tools/list shape + HTTPS endpoint per tool)","description":"Same tool set as openai-tools.json in MCP tools/list shape, each with the HTTPS endpoint it maps to, for tool routers and MCP server configs. Free, no payment.","responses":{"200":{"description":"The tool manifest (application/json; charset=utf-8)","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/.well-known/agent.json":{"get":{"tags":["discovery"],"summary":"Agent card alias (byte-identical to agent-card.json)","description":"Same payload as /.well-known/agent-card.json, served under the alternate well-known name some agent crawlers probe. Free, no payment.","responses":{"200":{"description":"The agent card (application/json; charset=utf-8)","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/.well-known/x402.json":{"get":{"tags":["discovery"],"summary":"x402 catalog alias (byte-identical to the extensionless catalog)","description":"Same payload as /.well-known/x402, served under the .json name some discovery crawlers probe. Free, no payment.","responses":{"200":{"description":"The x402 discovery catalog","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/.well-known/x402-resources":{"get":{"tags":["discovery"],"summary":"x402 catalog alias (byte-identical to the extensionless catalog)","description":"Same payload as /.well-known/x402, served under the x402-resources name some discovery crawlers probe. Free, no payment.","responses":{"200":{"description":"The x402 discovery catalog","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/x402-resources":{"get":{"tags":["discovery"],"summary":"x402 catalog alias (byte-identical to the extensionless catalog)","description":"Same payload as /.well-known/x402, served at the bare path some discovery crawlers probe. Free, no payment.","responses":{"200":{"description":"The x402 discovery catalog","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[]}},"/.well-known/security.txt":{"get":{"tags":["discovery"],"summary":"RFC 9116 security contact point","description":"Security researcher contact (GitHub advisories) with a rolling 1-year expiry. Free, no payment.","responses":{"200":{"description":"text/plain; charset=utf-8 — Contact / Expires / Preferred-Languages","content":{"text/plain":{"schema":{"type":"string"}}}}},"security":[]}},"/transparency":{"get":{"tags":["discovery"],"summary":"Public trust page: live revenue stats, prices, merchant wallet","description":"Human-readable trust page with live numbers from this deployment (paid calls settled, USDC revenue, 402 challenges served), the per-endpoint price list, and the merchant wallet. Free, no payment.","responses":{"200":{"description":"text/html; charset=utf-8 — the transparency page","content":{"text/html":{"schema":{"type":"string"}}}}},"security":[]}},"/.well-known/ai-plugin.json":{"get":{"tags":["discovery"],"summary":"AI plugin manifest (ChatGPT/Claude plugin discovery)","description":"ChatGPT/Claude-style plugin manifest pointing at this deployment's OpenAPI document, service icon, and contact info. Free, no payment.","responses":{"200":{"description":"application/json; charset=utf-8 — the plugin manifest","content":{"application/json":{"schema":{"type":"object","properties":{"schema_version":{"type":"string","example":"v1"},"name_for_human":{"type":"string","example":"webcap"},"name_for_model":{"type":"string","example":"webcap"},"description_for_human":{"type":"string"},"description_for_model":{"type":"string"},"auth":{"type":"object","properties":{"type":{"type":"string","example":"none"}}},"api":{"type":"object","properties":{"type":{"type":"string","example":"openapi"},"url":{"type":"string","example":"https://webcap.shoutsid.fyi/openapi.json"}}},"logo_url":{"type":"string"},"contact_email":{"type":"string"},"legal_info_url":{"type":"string"}}}}}}},"security":[]}},"/llms.txt":{"get":{"tags":["discovery"],"summary":"llms.txt convention document for LLM agents (markdown)","description":"The llms.txt document: what webcap is, how to pay (x402, USDC on Base, gasless EIP-3009, CDP facilitator), the paid endpoints with prices and request/response shapes, the free endpoints, pointers to /v1/x402/service and /openapi.json, and a minimal x402 payment flow. Free, no payment.","responses":{"200":{"description":"text/markdown; charset=utf-8 — the llms.txt document (base URL and prices from config)","content":{"text/markdown":{"schema":{"type":"string"}}}}},"security":[]}},"/skill.md":{"get":{"tags":["discovery"],"summary":"Installable agent skill file (markdown with YAML frontmatter)","description":"An agent skill (name: webcap) teaching how to call webcap with an x402 client (@x402/axios quick start, raw EIP-3009 fallback), with the pricing table and the free-preview alternative. Free, no payment.","responses":{"200":{"description":"text/markdown; charset=utf-8 — YAML frontmatter (name, description) + usage instructions","content":{"text/markdown":{"schema":{"type":"string"}}}}},"security":[]}},"/v1/register":{"post":{"tags":["accounts"],"summary":"Register a wallet and mint an API key (free, no auth)","description":"Idempotent per address: an existing account gets a fresh API key instead of a duplicate account. No payment: buy credits via POST /v1/invoice, or skip accounts entirely and pay per call over x402.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"properties":{"address":{"type":"string","description":"Ethereum address (hex, checksummed or not)"}}}}}},"responses":{"201":{"description":"Registered; {address, apiKey, balance}","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string"},"apiKey":{"type":"string","description":"Send as \"Authorization: Bearer <key>\" on subsequent requests"},"balance":{"type":"integer","example":0}}}}}},"422":{"description":"Missing or invalid address (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/invoice":{"post":{"tags":["accounts"],"summary":"Create a USDC credit invoice (manual ERC-20 transfer)","description":"Buy credits as USDC sent from your own wallet to the merchant address: the invoice names the token, the recipient, and the required amount for the chosen credit amount (credits omitted = 100). The API-key alternative to x402's per-call payment.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"credits":{"type":"integer","description":"Credits to buy (default 100)","example":100}}}}}},"responses":{"201":{"description":"Created; the USDC transfer details","content":{"application/json":{"schema":{"type":"object","properties":{"invoiceId":{"type":"string"},"merchant":{"type":"string","description":"Send the USDC to this address"},"token":{"type":"string","description":"USDC ERC-20 contract"},"chainId":{"type":"integer","example":8453},"requiredUsdc":{"type":"number","example":1},"credits":{"type":"integer","example":100},"expiresAt":{"type":"string"}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"credits must be a positive integer (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/capture":{"post":{"tags":["accounts"],"summary":"Capture a URL as a screenshot (paid, 1 credit per call)","description":"The credit-metered front door: charges 1 credit per capture (refunded when the capture fails). Prefer the x402 route for gasless USDC payment without an account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"The page to capture","example":"https://example.com/"},"format":{"type":"string","enum":["png","jpeg","pdf"],"description":"Screenshot format (default png)"},"options":{"type":"object","additionalProperties":false,"properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}}}}}}},"responses":{"200":{"description":"The artifact (base64) + its canonical public URL, plus the credit charge and remaining balance","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"format":{"type":"string","enum":["png","jpeg","pdf"]},"bytes":{"type":"integer","example":204800},"data":{"type":"string","description":"Base64-encoded image bytes"},"url":{"type":"string","example":"{publicBaseUrl}/v1/artifacts/{id}","description":"Canonical public artifact URL"}}},"creditsCharged":{"type":"integer","example":1},"balance":{"type":"integer","description":"Credits remaining after the charge"}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits; the error detail carries the 1-credit top-up invoice {invoiceId, requiredUsdc, balance} (error envelope, code insufficient_credits)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input: missing/invalid url, format or options. SSRF-blocked hosts 422 with detail {reason, dnsRebindingCaveat: true} (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-account spend cap exceeded (error envelope, code spend_cap_exceeded; detail {payer, spent, cap, reason}; WEBCAP_SPEND_CAP_CREDITS, unset means unlimited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/ledger":{"get":{"tags":["accounts"],"summary":"Merchant revenue ledger (summary + last 50 paid requests)","description":"Merchant-only: the P&L summary plus the 50 most recent paid requests (endpoint, payer, revenue and cost in USDC units).","responses":{"200":{"description":"Revenue summary + recent entries","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object"},"recent":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The authenticated account is not the merchant (error envelope, code forbidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/admin/hits/summary":{"get":{"tags":["accounts"],"summary":"Merchant hits summary (per-endpoint hits + paid counts + conversion)","description":"Merchant-only: endpoint_hits counts joined with revenue_ledger paid counts per endpoint (conversion = paid/hits, 0 when hits is 0).","responses":{"200":{"description":"Per-endpoint {endpoint, hits, paidCount, conversion}, most-hit first","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"hits":{"type":"integer"},"paidCount":{"type":"integer"},"conversion":{"type":"number"}}}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The authenticated account is not the merchant (error envelope, code forbidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/admin/analytics":{"get":{"tags":["accounts"],"summary":"Merchant time-series API usage analytics","description":"Merchant-only: hourly request counts, error rates, and latency over a configurable window (?hours=N, default 24, max 168). Includes top endpoints, top user agents, and aggregate totals.","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":168,"default":24},"description":"Hours of history to return (default 24, max 168)"}],"responses":{"200":{"description":"Time-series analytics: hourly buckets, top endpoints, top user agents, aggregate totals","content":{"application/json":{"schema":{"type":"object","properties":{"hoursBack":{"type":"integer"},"totalRequests":{"type":"integer"},"totalErrors":{"type":"integer"},"avgDurationMs":{"type":["number","null"]},"hourly":{"type":"array","items":{"type":"object","properties":{"time_bucket":{"type":"string","description":"ISO 8601 hourly bucket (e.g. 2026-09-08T14:00:00Z)"},"endpoint":{"type":"string"},"requests":{"type":"integer"},"errors":{"type":"integer"},"avg_duration_ms":{"type":["number","null"]},"p95_duration_ms":{"type":["number","null"]}}}},"topEndpoints":{"type":"array","items":{"type":"object","properties":{"endpoint":{"type":"string"},"requests":{"type":"integer"},"avgDurationMs":{"type":["number","null"]}}}},"topUserAgents":{"type":"array","items":{"type":"object","properties":{"userAgent":{"type":"string","description":"Normalized User-Agent (trimmed, capped at 200 chars; empty when absent)"},"requests":{"type":"integer"}}}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The authenticated account is not the merchant (error envelope, code forbidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/admin/waitlist":{"get":{"tags":["accounts"],"summary":"Merchant email waitlist for mailing list export","description":"Merchant-only: returns all waitlist signup emails with signup dates. Supports ?format=json (default) or ?format=csv for CSV export.","parameters":[{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"],"default":"json"},"description":"Response format: json (default) or csv"}],"responses":{"200":{"description":"Waitlist entries with email and signup date","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of waitlist entries"},"entries":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string","format":"email"},"signedUpAt":{"type":"string","description":"ISO 8601 timestamp"}}}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The authenticated account is not the merchant (error envelope, code forbidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/og":{"get":{"tags":["discovery"],"summary":"Free Open Graph metadata for a URL (no payment)","description":"Fetch the page and return its title/description/image (twitter: preferred over og:, falling back to the <title> tag and icon link) plus twitter:card/site/creator and article:published_time/author/section/tag. Free, no payment and no API key.","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string"},"description":"The page to fetch"}],"responses":{"200":{"description":"The Open Graph metadata (absent fields are omitted)","content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"image":{"type":"string"},"icon":{"type":"string"},"twitterCard":{"type":"string"},"twitterSite":{"type":"string"},"twitterCreator":{"type":"string"},"twitterTitle":{"type":"string"},"twitterDescription":{"type":"string"},"twitterImage":{"type":"string"},"articlePublishedTime":{"type":"string"},"articleAuthor":{"type":"string"},"articleSection":{"type":"string"},"articleTags":{"type":"array","items":{"type":"string"}}}}}}},"422":{"description":"Missing or invalid url query parameter (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream page capture failed (error envelope, code capture_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}},"/v1/account":{"get":{"tags":["accounts"],"summary":"Account balance + invoice history","responses":{"200":{"description":"The address, credit balance, and the account's invoices","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string"},"balance":{"type":"integer","description":"Credits remaining"},"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"credits":{"type":"number"},"requiredUsdc":{"type":"number"},"createdAt":{"type":"string"}}}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/webhooks":{"post":{"tags":["accounts"],"summary":"Register a payment webhook (Bearer auth)","description":"Register an HTTPS webhook URL to receive payment events. The webhook fires on payment.settled events. A signing secret is returned on creation (shown only once). The webhook body is signed with HMAC-SHA256.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"HTTPS webhook URL"}}}}}},"responses":{"201":{"description":"Webhook registered; secret shown only on creation","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"example":["payment.settled"]},"secret":{"type":"string","description":"HMAC signing secret (shown only on creation)"}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"url is required and must be https (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]},"get":{"tags":["accounts"],"summary":"List payment webhooks (Bearer auth)","description":"List all payment webhooks for the authenticated account.","responses":{"200":{"description":"List of webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/webhooks/{id}":{"delete":{"tags":["accounts"],"summary":"Deactivate a payment webhook (Bearer auth)","description":"Soft-delete a payment webhook by deactivating it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Webhook ID"}],"responses":{"200":{"description":"Webhook deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"apiKey":[]}]}},"/v1/capture/jobs":{"post":{"tags":["capture"],"summary":"Enqueue an async capture job (paid once at submit)","description":"Enqueue a screenshot capture as a background job: the submit charges once (1 credit on the API-key rail, 0.001 USDC over x402) and returns 202 {jobId, status}. Poll GET /v1/capture/jobs/{id} (free, no auth) until status is completed|failed. An optional https webhookUrl receives the terminal delivery (signed with x-hub-signature-256 when a job secret is configured).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"The page to capture","example":"https://example.com/"},"format":{"type":"string","enum":["png","jpeg","pdf"],"description":"Screenshot format (default png)"},"options":{"type":"object","additionalProperties":false,"properties":{"timeoutMs":{"type":"integer","description":"Page load timeout in milliseconds"},"fullPage":{"type":"boolean","description":"Capture the full scrollable page"},"viewport":{"type":"object","description":"Capture viewport in CSS pixels (clamped to 320-3840 wide, 320-2160 tall)","properties":{"width":{"type":"integer","description":"Viewport width in CSS pixels"},"height":{"type":"integer","description":"Viewport height in CSS pixels"}}},"deviceScaleFactor":{"type":"number","description":"Device pixel ratio (clamped to at most 3)"},"isMobile":{"type":"boolean","description":"Render with a mobile viewport"},"userAgent":{"type":"string","description":"Custom user agent string"},"proxy":{"type":"string","description":"Proxy: \"auto\", \"stealth\", or an http(s) proxy URL string"},"maxContentWords":{"type":"integer","description":"Word budget for the extracted main content (paragraphs + markdown), clamped to 25..100000. Lets an agent size a page to its context window; content is cut at a block boundary and content.truncated reports it."},"waitFor":{"type":"object","description":"Wait for a selector before capture (timeoutMs capped at 10000)","properties":{"selector":{"type":"string","description":"CSS selector to wait for"},"timeoutMs":{"type":"integer","description":"Wait timeout in milliseconds (capped at 10000)"}}},"actions":{"type":"array","description":"Post-load actions: click/type/wait objects (1 to 5)","items":{"type":"object","properties":{"type":{"type":"string","enum":["click","type","wait"],"description":"Action kind"},"selector":{"type":"string","description":"CSS selector (click/type)"},"text":{"type":"string","description":"Text to type (type only)"},"timeoutMs":{"type":"integer","description":"Wait duration in milliseconds (wait only, capped at 10000)"}}},"minItems":1,"maxItems":5}}},"webhookUrl":{"type":"string","description":"Optional https URL receiving the terminal delivery ({jobId, status, artifactUrl|error}); signed with x-hub-signature-256 when a job secret is configured"}}}}}},"responses":{"202":{"description":"Enqueued; poll GET /v1/capture/jobs/{id} for the terminal state","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]}}}}}},"401":{"description":"Missing or invalid Bearer API key (error envelope, code unauthorized)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required: insufficient credits on the API-key rail (error envelope, code insufficient_credits; detail {invoiceId, requiredUsdc, balance} names the 1-credit top-up invoice), or the x402 challenge on x402 deployments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input: missing/invalid url, format, or webhookUrl (error envelope, code unprocessable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Spend cap exceeded (error envelope, code spend_cap_exceeded; detail {payer, spent, cap, reason}; per-payer USDC units on x402, per-account credits on the API-key rail; unset cap means unlimited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.001000"},"protocols":[{"x402":{}},{"mpp":{"method":"evm"}}]}}},"/v1/capture/jobs/{id}":{"get":{"tags":["capture"],"summary":"Poll an async capture job (free, no auth)","description":"Free status poll: queued|processing while the capture runs, completed with result.artifactUrl, or failed with error. Terminal states carry the payment receipt (payer, priceUsdcUnits, plus creditsUsed/costUsdcUnits where applicable). The artifact URL accepts optional signed query ?exp=&sig= (HMAC-SHA256 over \"<id>.<exp>\"): bad signatures 403 (code forbidden), expired ones 410 (code gone); unsigned fetches keep serving with a Deprecation header.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The job state, incl. the payment receipt and (when completed) the artifact URL","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","processing","completed","failed"]},"payment":{"type":"object","properties":{"payer":{"type":"string"},"priceUsdcUnits":{"type":"integer","example":1000},"creditsUsed":{"type":"integer","example":1},"costUsdcUnits":{"type":"integer"}}},"result":{"type":"object","description":"Present when status=completed","properties":{"artifactUrl":{"type":"string","description":"Canonical public artifact URL"}}},"error":{"type":"string","description":"Present when status=failed"}}}}}},"404":{"description":"Unknown job id (error envelope, code not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[]}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"Authorization","description":"API key from POST /v1/register, sent as `Authorization: Bearer <key>`. Not needed on the x402 routes, which settle per call instead."}},"schemas":{"Error":{"type":"object","description":"The webcap error envelope returned for every non-2xx response","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"bad_request"},"message":{"type":"string","example":"url is required"},"detail":{"type":"object","additionalProperties":true,"description":"Optional structured detail"}}}}},"OpenapiDocument":{"type":"object","description":"An OpenAPI 3.1 document (this catalog)","properties":{"openapi":{"type":"string","example":"3.1.0"},"info":{"type":"object"},"servers":{"type":"array"},"paths":{"type":"object"}}}}}}