{"components":{"schemas":{"AskRequest":{"additionalProperties":false,"description":"Exactly one of job_id or context.","properties":{"context":{"description":"The Context of a finished job, exactly as it was returned (at most 1 MB). A link or a file reference is not a Context: it is refused with 400.","type":"object"},"job_id":{"type":"string"},"question":{"maxLength":8000,"minLength":1,"type":"string"}},"required":["question"],"type":"object"},"AskResponse":{"properties":{"answer":{"type":"string"},"citations":{"items":{"properties":{"end":{"type":"number"},"start":{"type":"number"},"text":{"type":"string"}},"required":["start","end","text"],"type":"object"},"type":"array"},"minutes_charged":{"type":"number"},"notes":{"items":{"type":"string"},"type":"array"}},"required":["answer","citations","minutes_charged"],"type":"object"},"BillingOffer":{"properties":{"configured":{"description":"False while payments are not set up at all.","type":"boolean"},"plans":{"description":"Only what the caller can buy now. Empty when nothing is on sale.","properties":{"mac-lifetime":{"properties":{"amount":{"description":"What the next buyer pays, in cents.","type":"integer"},"buyers":{"description":"Paid purchases, net of refunds and disputes.","type":"integer"},"currency":{"const":"usd"},"earlyBird":{"properties":{"left":{"type":"integer"},"places":{"type":"integer"}},"type":["object","null"]},"listAmount":{"type":"integer"},"preview":{"type":"boolean"}},"type":"object"},"pro":{"properties":{"intervals":{"items":{"properties":{"amount":{"type":"integer"},"currency":{"const":"usd"},"interval":{"enum":["month","year"]}},"type":"object"},"type":"array"},"preview":{"type":"boolean"}},"type":"object"},"topup":{"properties":{"captionPacks":{"items":{"properties":{"amount":{"type":"integer"},"captions":{"type":"integer"},"currency":{"const":"usd"},"id":{"enum":["captions-200","captions-1200","captions-5000"]}},"type":"object"},"type":"array"},"packs":{"items":{"properties":{"amount":{"type":"integer"},"currency":{"const":"usd"},"id":{"enum":["small","medium","large"]},"minutes":{"type":"integer"}},"type":"object"},"type":"array"},"preview":{"type":"boolean"}},"type":"object"}},"type":"object"},"signInRequired":{"type":"boolean"},"taxAtCheckout":{"type":"boolean"}},"required":["configured","signInRequired","taxAtCheckout","plans"],"type":"object"},"CheckoutConfirmed":{"properties":{"captionCredits":{"description":"Caption wallet balance for a caption purchase; null for other purchases.","type":["integer","null"]},"kind":{"enum":["pro","mac-lifetime","topup"]},"lifetimeMac":{"type":"boolean"},"minutesTopup":{"type":"number"},"paid":{"description":"True when Stripe says the checkout was paid. A `pending` state with `paid: false` is not a payment (the checkout is still open, or Stripe could not be asked): do not tell the buyer one went through.","type":"boolean"},"plan":{"enum":["free","pro","lifetime"]},"planUntil":{"type":["number","null"]},"state":{"enum":["active","pending","expired","failed"]}},"required":["state","kind","plan","planUntil","lifetimeMac","minutesTopup","paid"],"type":"object"},"CheckoutRequest":{"additionalProperties":false,"properties":{"datafastSessionId":{"type":"string"},"datafastVisitorId":{"type":"string"},"interval":{"description":"Pro only. Default month.","enum":["month","year"]},"pack":{"description":"Extra minutes only.","enum":["small","medium","large"]},"plan":{"enum":["pro","mac-lifetime","topup"]},"ref":{"description":"Where the visitor came from (1 to 40 of a-z, 0-9, dot, dash, underscore).","type":"string"}},"required":["plan"],"type":"object"},"CheckoutStarted":{"properties":{"amount":{"description":"What this Checkout page charges, in cents, before tax: the price the buyer will see there. Compare it with the price the page showed before sending the buyer on.","type":"integer"},"currency":{"description":"Lower case ISO code, `usd`.","type":"string"},"earlyBird":{"description":"True when the page is at the Mac Lifetime early bird price.","type":"boolean"},"expiresAt":{"description":"Epoch milliseconds.","type":"number"},"sessionId":{"type":"string"},"url":{"description":"A Stripe Checkout URL.","type":"string"}},"required":["url","sessionId","expiresAt","amount","currency","earlyBird"],"type":"object"},"ContactRequest":{"properties":{"email":{"description":"Where to answer.","format":"email","maxLength":254,"type":"string"},"message":{"maxLength":5000,"minLength":10,"type":"string"},"topic":{"enum":["question","privacy","copyright","abuse","security"]},"website":{"description":"Decoy for bots. Leave it empty.","type":"string"}},"required":["topic","email","message"],"type":"object"},"Context":{"description":"The result of a video: transcript, what was on screen, summary and chapters. Times are seconds. The same JSON the CLI writes with --format json.","properties":{"coverage":{"type":"object"},"id":{"description":"youtube:<id>, instagram:<id>, ... or local:<key> for an upload.","type":"string"},"meta":{"type":"object"},"produced":{"type":"object"},"schema":{"const":1},"source":{"type":"object"},"synthesis":{"type":["object","null"]},"tiers":{"items":{"type":"object"},"type":"array"},"transcript":{"type":["object","null"]},"usage":{"type":"object"},"visual":{"type":["object","null"]},"warnings":{"items":{"type":"object"},"type":"array"}},"required":["schema","id","meta","source","transcript","visual","synthesis","tiers","usage","warnings"],"type":"object"},"ContextOptions":{"additionalProperties":false,"properties":{"detail":{"enum":["transcript","brief","full"]},"diarize":{"oneOf":[{"type":"boolean"},{"const":"auto"}]},"effort":{"deprecated":true,"description":"Removed on 2026-10-09. Still accepted, whatever it holds, and ignored: the job runs as it would without it. Send `detail: \"transcript\"` for the words alone and `proofread: \"full\"` for the full proofread."},"include_words":{"description":"Accounts only. Keep the word list in the result.","type":"boolean"},"proofread":{"oneOf":[{"type":"boolean"},{"enum":["terms","full","auto"]}]},"quality":{"description":"Accounts only.","enum":["standard","high"]},"visual":{"additionalProperties":false,"description":"Accounts only.","properties":{"fps":{"maximum":2,"minimum":0.1,"type":"number"},"max_scenes":{"maximum":200,"minimum":1,"type":"integer"}},"type":"object"},"vocabulary":{"description":"Accounts only.","items":{"maxLength":100,"type":"string"},"maxItems":50,"type":"array"}},"type":"object"},"ContextRequest":{"additionalProperties":false,"description":"Exactly one of url or upload_id.","properties":{"language":{"oneOf":[{"type":"string"},{"items":{"type":"string"},"maxItems":5,"type":"array"}]},"mode":{"default":"auto","enum":["auto","captions","audio","visual","full","listen","watch","both"]},"options":{"$ref":"#/components/schemas/ContextOptions"},"turnstile_token":{"description":"Required for anonymous Listen and Watch when the server has Turnstile on.","type":"string"},"upload_id":{"type":"string"},"url":{"maxLength":2048,"type":"string"}},"type":"object"},"Error":{"properties":{"error":{"additionalProperties":true,"properties":{"code":{"description":"snake_case.","type":"string"},"engineCode":{"description":"The engine's own code (QUOTA, SOURCE_UNAVAILABLE, ...) when there is one.","type":"string"},"hint":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"type":"object"}},"required":["error"],"type":"object"},"ErrorInfo":{"description":"The engine's error: what a failed job carries and what the SSE `error` event holds.","properties":{"code":{"type":"string"},"hint":{"type":"string"},"message":{"type":"string"},"retryAfterMs":{"type":"number"},"retryable":{"type":"boolean"},"scope":{"enum":["job","queue","account"]},"stage":{"type":"string"},"status":{"type":"number"}},"required":["code","message","retryable","scope","stage"],"type":"object"},"Job":{"properties":{"attempt":{"type":"integer"},"cache_hit":{"type":["boolean","null"]},"cancel_requested":{"type":"boolean"},"created_at":{"description":"Epoch milliseconds.","type":"integer"},"degraded":{"type":"object"},"error":{"oneOf":[{"$ref":"#/components/schemas/ErrorInfo"},{"type":"null"}]},"estimate":{"oneOf":[{"$ref":"#/components/schemas/Quote"},{"type":"null"}]},"events_url":{"type":"string"},"expires_at":{"type":"integer"},"finished_at":{"type":["integer","null"]},"input":{"type":"object"},"job_id":{"type":"string"},"minutes_charged":{"type":["number","null"]},"mode":{"type":"string"},"progress":{"description":"0 to 1, never going backwards; null until known.","type":["number","null"]},"result":{"oneOf":[{"$ref":"#/components/schemas/Context"},{"type":"null"}]},"result_url":{"type":"string"},"stage":{"type":["string","null"]},"started_at":{"type":["integer","null"]},"status":{"enum":["queued","running","succeeded","failed","canceled"]}},"required":["job_id","status","mode","estimate","result","error","events_url","result_url","created_at","expires_at"],"type":"object"},"JobCreated":{"properties":{"degraded":{"type":"object"},"estimate":{"$ref":"#/components/schemas/Quote"},"events_url":{"type":"string"},"idempotent_replay":{"type":"boolean"},"job_id":{"type":"string"},"mode":{"type":"string"},"queue_position":{"type":"integer"},"result_url":{"type":"string"},"status":{"type":"string"},"upload_id":{"type":"string"}},"required":["job_id","status","events_url","result_url","estimate"],"type":"object"},"Ok":{"properties":{"ok":{"const":true}},"required":["ok"],"type":"object"},"Quote":{"properties":{"cached":{"description":"Everything is already stored: nothing is charged.","type":"boolean"},"caption_credits":{"description":"Maximum caption credits for a strict YouTube request. Media minutes are zero.","minimum":0,"type":"integer"},"charges_as":{"enum":["caption-item","captions","audio","visual","full"]},"length_known":{"const":false,"description":"Present (false) when the length of the source could not be read before the run (a YouTube link that yt-dlp and the YouTube page both refused; a direct media link not yet downloaded). The job is charged from the length it measures."},"max_media_seconds":{"description":"Only with `length_known: false` on a YouTube link: the longest stretch the job reads (a clip that ends there): never more than the plan allows per video and never more than the minutes left. `minutes` is the most the job can then use. When the server can get the video's own captions they answer instead, also for a longer video, and the job costs less.","type":"number"},"media_seconds":{"description":"Seconds of media the quote is for; null when the length is not known (`length_known: false`).","type":["number","null"]},"minutes":{"description":"Minutes this job uses when it runs as quoted (0 when cached), including the half minute for each media minute when the full proofread is part of it (`proofread: \"full\"` or a `vocabulary`). It is charged only if the pass runs. With `length_known: false`: the most it can use.","type":"number"},"tiers":{"items":{"enum":["captions","audio","visual","synthesis"]},"type":"array"}},"required":["minutes","media_seconds","tiers","cached","charges_as"],"type":"object"},"Status":{"properties":{"free_tier":{"properties":{"listen_watch":{"enum":["open","paused"]}},"type":"object"},"notes":{"description":"What is working through a fallback, in words (for example results that came from captions or a direct read of the link because the platform blocks downloads from this server).","items":{"type":"string"},"type":"array"},"ok":{"const":true},"queue":{"properties":{"capacity":{"type":"integer"},"queued":{"type":"integer"},"running":{"type":"integer"}},"type":"object"},"sources":{"description":"Per source over the window: jobs, succeeded, failed, blocked (failed because the platform refused), fallback (succeeded through captions or a direct read of the link after the platform blocked the download: a success with a note), success_rate, avg_seconds.","items":{"type":"object"},"type":"array"},"status":{"enum":["operational","degraded"]},"time":{"type":"integer"},"window_hours":{"type":"integer"}},"required":["ok","status","queue","sources"],"type":"object"},"Upload":{"properties":{"bytes":{"type":"integer"},"duration_seconds":{"type":["number","null"]},"expires_at":{"type":"integer"},"file_name":{"type":["string","null"]},"has_audio":{"type":"boolean"},"has_video":{"type":"boolean"},"mime":{"type":["string","null"]},"upload_id":{"type":"string"}},"required":["upload_id","bytes","has_audio","has_video","expires_at"],"type":"object"},"WaitlistJoined":{"properties":{"confirm_by_email":{"description":"True when this server sends a confirmation email to an address that joins. It is a fact about the server: the same for every caller and every address, and it never says whether the address was on the list.","type":"boolean"},"ok":{"const":true}},"required":["ok","confirm_by_email"],"type":"object"},"WaitlistLeaveRequest":{"properties":{"email":{"format":"email","maxLength":254,"type":"string"},"website":{"description":"Decoy for bots. Leave it empty.","type":"string"}},"required":["email"],"type":"object"},"WaitlistRequest":{"properties":{"email":{"format":"email","maxLength":254,"type":"string"},"interest":{"enum":["mac","lifetime","pro","cli"]},"path":{"description":"The site page the visitor was on, such as /download.","maxLength":200,"type":"string"},"ref":{"description":"Where the visitor first came from (`?ref=`): lower case letters, digits, dot, dash, underscore.","maxLength":40,"type":"string"},"website":{"description":"Decoy for bots. Leave it empty.","type":"string"}},"required":["email","interest"],"type":"object"}},"securitySchemes":{"apiKey":{"description":"An API key (sbz_live_...), as X-API-Key. Keys are made in the dashboard or by `scribiz login`. Each key has one scope, named in an operation's security requirement. `all`: Everything a key can do: start jobs, upload, ask, cancel, the Gemini proxy and listing keys. Includes read. `mcp`: The remote MCP server at /mcp. Includes read. `read`: Read jobs and their events.","in":"header","name":"X-API-Key","type":"apiKey","x-scopes":{"all":"Everything a key can do: start jobs, upload, ask, cancel, the Gemini proxy and listing keys. Includes read.","mcp":"The remote MCP server at /mcp. Includes read.","read":"Read jobs and their events."}},"bearer":{"description":"An API key (sbz_live_...) or a session token from the device flow. Each key has one scope, named in an operation's security requirement. `all`: Everything a key can do: start jobs, upload, ask, cancel, the Gemini proxy and listing keys. Includes read. `mcp`: The remote MCP server at /mcp. Includes read. `read`: Read jobs and their events. A session token has every scope.","scheme":"bearer","type":"http","x-scopes":{"all":"Everything a key can do: start jobs, upload, ask, cancel, the Gemini proxy and listing keys. Includes read.","mcp":"The remote MCP server at /mcp. Includes read.","read":"Read jobs and their events."}},"cookie":{"description":"The browser session (same-origin /api/*).","in":"cookie","name":"better-auth.session_token","type":"apiKey"}}},"info":{"description":"Scribiz returns the context of any video: transcript, what was on screen, summary and chapters. Errors are always {\"error\": {\"code\", \"message\", ...}}. Usage is counted in minutes.","title":"Scribiz API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/auth/device/code":{"post":{"description":"RFC 8628 device authorization. `client_id` is scribiz-cli, scribiz-mac or scribiz-mcp.","operationId":"deviceCode","requestBody":{"content":{"application/json":{"schema":{"properties":{"client_id":{"type":"string"}},"required":["client_id"],"type":"object"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Device and user codes."}},"summary":"Start a device login.","tags":["Auth"]}},"/auth/device/token":{"post":{"operationId":"deviceToken","requestBody":{"content":{"application/json":{"schema":{"properties":{"client_id":{"type":"string"},"device_code":{"type":"string"},"grant_type":{"const":"urn:ietf:params:oauth:grant-type:device_code"}},"required":["grant_type","device_code","client_id"],"type":"object"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"A session token."},"400":{"description":"authorization_pending, slow_down, expired_token, access_denied"}},"summary":"Poll for the device login result.","tags":["Auth"]}},"/auth/sign-in/magic-link":{"post":{"operationId":"magicLink","requestBody":{"content":{"application/json":{"schema":{"properties":{"callbackURL":{"type":"string"},"email":{"format":"email","type":"string"}},"required":["email"],"type":"object"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Sent."}},"summary":"Email a sign-in link.","tags":["Auth"]}},"/health":{"get":{"operationId":"health","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"ok":{"const":true}},"type":"object"}}},"description":"Alive."}},"summary":"Liveness.","tags":["Service"]}},"/mcp":{"post":{"description":"Remote MCP, stateless Streamable HTTP. Without credentials (the free tier): captions when the video has them, videos that were already processed, and otherwise a model reads the link (approximate timing, no speaker labels), inside the same small daily allowance as the free web tools and for videos up to 15 minutes; never the on-screen layer. When the allowance is used up it falls back to captions and processed videos. With `Authorization: Bearer sbz_...`: the plan's limits. Tools: get_transcript, get_video_context, ask_video, search_video, get_job. A browser that opens this URL (GET with `Accept: text/html`) is redirected (303) to the MCP page on the site.","operationId":"mcp","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"A JSON-RPC message."},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}},"text/event-stream":{"schema":{"type":"string"}}},"description":"JSON-RPC result, or an event stream with progress."},"401":{"description":"Invalid API key (WWW-Authenticate: Bearer error=\"invalid_token\")."}},"security":[{"bearer":["mcp"]},{}],"summary":"Model Context Protocol endpoint.","tags":["MCP"]},"servers":[{"description":"The site root.","url":"https://scribiz.com"}]},"/v1/account":{"delete":{"operationId":"deleteAccount","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"ok":{"const":true}},"type":"object"}}},"description":"Deleted."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"bearer":[]},{"cookie":[]}],"summary":"Delete the account and everything it owns.","tags":["Account"]}},"/v1/ask":{"post":{"description":"One question about a Context. It costs 0.1 minute, or what it cost when that is more (priced like a proxied call, at $0.005 a minute): a question about a long video costs more than one about a short one. The check before the question uses the size of the Context. Never starts the on-screen layer.","operationId":"ask","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskRequest"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskResponse"}}},"description":"Answered."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"quota_exceeded"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"job_not_ready"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"body_too_large"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]},{}],"summary":"Ask a question about a video.","tags":["Jobs"]}},"/v1/billing/checkout":{"post":{"description":"Start a purchase with Stripe. Needs a signed-in session (a browser session or the session token of the device flow), not an API key. Send a plan name and nothing else: the price, the amount and the URLs are decided by the server, and a body with any other field is refused. The answer is the Stripe Checkout URL to send the buyer to; it is always a URL on stripe.com that Stripe returned. Until the owner switches payments on, and for any plan with no price set, this answers 501 `billing_not_configured`. 409 `not_on_sale` (set up but not open to the public), `already_owned` (Mac Lifetime is already on the account) or `already_subscribed` (Pro is already running: the error has `portal: true`). At most 10 starts an hour per account.","operationId":"billingCheckout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutStarted"}}},"description":"The Checkout page to send the buyer to."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"session_required, bad_origin"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_on_sale, already_owned, already_subscribed"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"body_too_large"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request: the plan, interval or pack is not valid (`error.field` says which)."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_not_configured: paid plans are not open for purchase yet."},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"stripe_unavailable: nothing was charged."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_misconfigured, busy"}},"security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]},{}],"summary":"Start a purchase.","tags":["Billing"]}},"/v1/billing/checkout/confirm":{"post":{"description":"After the buyer comes back from Stripe: apply the purchase now when the webhook is late. Idempotent. A Stripe failure is `pending`, never an error. Only for the account that started that checkout (anything else is 404). At most 120 an hour per account.","operationId":"billingCheckoutConfirm","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"sessionId":{"description":"The `cs_...` id from the success URL.","type":"string"}},"required":["sessionId"],"type":"object"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutConfirmed"}}},"description":"Where the purchase stands."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"session_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_not_configured"}},"security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]},{}],"summary":"Confirm a purchase after Stripe sends the buyer back.","tags":["Billing"]}},"/v1/billing/offer":{"get":{"description":"What can be bought right now, with the prices. Public. `plans` holds only what the caller can buy: with nothing on sale it is empty (`configured: false` while payments are not set up at all). A signed-in preview account also sees a configured plan that is not public yet, marked `preview: true`. `mac-lifetime.amount` is what the next buyer pays (the early bird price while a place is left, otherwise the list price). Amounts are USD cents. Cached for 15 seconds for anonymous callers; at most 60 requests a minute per visitor.","operationId":"billingOffer","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingOffer"}}},"description":"The offer."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]},{}],"summary":"What is on sale.","tags":["Billing"]}},"/v1/billing/portal":{"post":{"description":"Open Stripe's billing portal for the account (receipts, card, cancelling Pro). Needs a signed-in session, not an API key. The answer is a short-lived stripe.com URL. 409 `no_billing_account` when the account has never started a checkout. At most 20 an hour per account.","operationId":"billingPortal","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"url":{"type":"string"}},"required":["url"],"type":"object"}}},"description":"The portal URL."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"session_required, bad_origin"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"no_billing_account"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_not_configured"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"stripe_unavailable"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_misconfigured"}},"security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]},{}],"summary":"Open the billing portal.","tags":["Billing"]}},"/v1/billing/webhook":{"post":{"description":"For Stripe only. Stripe signs every call (`Stripe-Signature`, HMAC-SHA256 over the timestamp and the raw body, five minute window) and a call without a good signature is refused with 400 before anything is read. No account, key or cookie is involved and nothing here is for programs. 501 `billing_not_configured` while payments are not set up.","operationId":"billingWebhook","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"A Stripe event."},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"received":{"const":true}},"type":"object"}}},"description":"Received."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_signature, invalid_payload"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"body_too_large"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited: too many bad signatures from one address."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"retry_later: not handled yet, Stripe sends it again."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"billing_not_configured"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"busy"}},"summary":"Stripe's events.","tags":["Billing"]}},"/v1/cli/exchange":{"post":{"description":"Exchanges the session token of the device flow for a long-lived API key (and revokes that session).","operationId":"cliExchange","requestBody":{"content":{"application/json":{"schema":{"properties":{"deviceLabel":{"type":"string"},"scopes":{"enum":["all","mcp"]}},"type":"object"}}},"description":"JSON"},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The key, shown once."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"key_limit_reached"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"bearer":[]}],"summary":"Finish `scribiz login`.","tags":["Account"]}},"/v1/contact":{"post":{"description":"Send a message to the maker. No account needed. Open to our own pages and to programs; a request from a page on another site is refused. `website` is a decoy field for bots: leave it empty. The message is stored for the owner to read; nothing is sent back, so give an address to answer. At most 3 messages an hour per visitor, and 60 an hour for the whole site.","operationId":"sendContact","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}},"description":"Received."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"bad_origin"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request: a field is not valid (`error.field` says which)."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"summary":"Send a message.","tags":["Contact"]}},"/v1/context":{"post":{"description":"Quotes the job (length, minutes, plan limits), then queues it. The 202 body says what it will cost; follow `events_url` for progress and `result_url` for the Context.","operationId":"createContext","parameters":[{"description":"Repeats of the same key with the same body return the same job.","in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":200,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextRequest"}}},"description":"JSON"},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCreated"}}},"description":"Queued."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"quota_exceeded: out of minutes"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"video_too_long"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The source cannot be read (source_unavailable, url_unsupported, ...)."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"},"451":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"blocked_content"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"busy, not_configured"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]},{}],"summary":"Start a job for a link or an upload.","tags":["Jobs"]}},"/v1/gemini/v1beta/interactions":{"post":{"description":"The metered Gemini proxy the engine uses with `createTransport({kind: \"hosted\"})`. Only the Files and Interactions paths of an allow-list are served, charged from what Google reports. Needs an API key with the `all` scope.","operationId":"geminiInteractions","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Google response."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"quota_exceeded"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]}],"summary":"Gemini proxy: create an interaction.","tags":["Gemini proxy"]}},"/v1/jobs":{"get":{"description":"The account's jobs that are running or still have a result, newest first, without the results themselves (read one with GET /v1/jobs/{id}). `via` names the messenger a job came in through (telegram or whatsapp), null otherwise. Runs through the MCP server are not listed. Needs an account: a job started without one is reached by its link.","operationId":"listJobs","parameters":[{"description":"Jobs on a page (1 to 100, default 50).","in":"query","name":"limit","schema":{"maximum":100,"minimum":1,"type":"integer"}},{"description":"Only jobs created before this time (epoch ms): pass `next_before` of the page before.","in":"query","name":"before","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"jobs":{"items":{"properties":{"created_at":{"type":"integer"},"error_code":{"type":["string","null"]},"expires_at":{"type":"integer"},"file_name":{"type":["string","null"]},"finished_at":{"type":["integer","null"]},"input":{"type":"object"},"job_id":{"type":"string"},"media_seconds":{"type":["number","null"]},"minutes_charged":{"type":["number","null"]},"mode":{"type":"string"},"provider":{"type":["string","null"]},"status":{"enum":["queued","running","succeeded","failed","canceled"],"type":"string"},"title":{"type":["string","null"]},"via":{"type":["string","null"]}},"type":"object"},"type":"array"},"next_before":{"description":"Null on the last page.","type":["integer","null"]}},"type":"object"}}},"description":"One page of jobs."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["read"]},{"bearer":["read"]},{"cookie":[]}],"summary":"List the account's jobs.","tags":["Jobs"]}},"/v1/jobs/{id}":{"delete":{"description":"Cancels a job that is queued or running; deletes the result of one that has finished.","operationId":"cancelJob","parameters":[{"description":"The job id (job_...).","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Cancelled or deleted."},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}},"description":"Cancel requested; the job is still winding down."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]},{}],"summary":"Cancel or delete a job.","tags":["Jobs"]},"get":{"description":"An account's job is readable by that account only. An anonymous job is readable by whoever holds its id, for 24 hours.","operationId":"getJob","parameters":[{"description":"The job id (job_...).","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}},"description":"The job."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"job_expired"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["read"]},{"bearer":["read"]},{"cookie":[]},{}],"summary":"State, progress and the Context when done.","tags":["Jobs"]}},"/v1/jobs/{id}/events":{"get":{"description":"Server-sent events. `id` is the event number (resume with Last-Event-ID), `event` is the type, `data` is the engine's ProgressEvent as JSON. A `: ping` comment every 15 s. The stream ends after `done` or `error`.","operationId":"jobEvents","parameters":[{"description":"The job id (job_...).","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"header","name":"Last-Event-ID","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"text/event-stream":{"schema":{"type":"string"}}},"description":"The event stream."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["read"]},{"bearer":["read"]},{"cookie":[]},{}],"summary":"Progress events of a job.","tags":["Jobs"]}},"/v1/keys":{"get":{"operationId":"listKeys","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Keys (without secrets)."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]}],"summary":"List API keys.","tags":["Account"]},"post":{"operationId":"createKey","requestBody":{"content":{"application/json":{"schema":{"properties":{"expiresInDays":{"type":"integer"},"name":{"type":"string"},"scopes":{"enum":["all","mcp","read"]}},"type":"object"}}},"description":"JSON"},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The key, shown once."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"key_limit_reached"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"bearer":[]},{"cookie":[]}],"summary":"Create an API key.","tags":["Account"]}},"/v1/keys/{id}":{"delete":{"operationId":"revokeKey","parameters":[{"description":"The key id.","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Revoked."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"bearer":[]},{"cookie":[]}],"summary":"Revoke an API key.","tags":["Account"]}},"/v1/me":{"get":{"description":"Who the caller is, the plan, minutes used and left, and every limit. Anonymous callers get their daily bucket.","operationId":"me","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The caller."}},"security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]},{}],"summary":"Account, plan and usage.","tags":["Account"]}},"/v1/openapi.json":{"get":{"operationId":"openapi","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"This document."}},"summary":"The OpenAPI document.","tags":["Service"]}},"/v1/status":{"get":{"description":"Queue depth, success rate per source over the last day, and whether the free Listen/Watch tier is paused.","operationId":"status","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Status."}},"summary":"Service status.","tags":["Service"]}},"/v1/transcribe":{"post":{"description":"Multipart convenience: send the file as the `file` part (at most 100 MB) with optional `mode`, `language`, `options` (JSON) and `turnstile_token`. Returns the same 202 as POST /v1/context, plus `upload_id`. For larger or streamed files use PUT /v1/uploads.","operationId":"transcribe","requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"file":{"contentMediaType":"application/octet-stream","type":"string"},"language":{"type":"string"},"mode":{"enum":["auto","captions","audio","visual","full","listen","watch","both"]},"options":{"description":"JSON object as in ContextOptions.","type":"string"},"turnstile_token":{"type":"string"}},"required":["file"],"type":"object"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCreated"}}},"description":"Queued."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"quota_exceeded"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"411":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"length_required"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"body_too_large, video_too_long"},"415":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unsupported_media"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]},{}],"summary":"Upload a file and start a job.","tags":["Uploads"]}},"/v1/uploads":{"put":{"description":"Streams the body to the server. Content-Length is required, 100 MB at most; the file lives 60 minutes and ffprobe must recognise a media container. Then POST /v1/context {\"upload_id\"}.","operationId":"upload","parameters":[{"in":"header","name":"X-File-Name","required":false,"schema":{"type":"string"}},{"in":"header","name":"Content-Length","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}},"description":"Received."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unauthorized, invalid_api_key or invalid_token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"insufficient_scope, session_required, account_required, turnstile_required, bad_origin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"411":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"length_required"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"body_too_large"},"415":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"unsupported_media"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited, too_many_in_flight, anonymous_limit, daily_spend_limit"}},"security":[{"apiKey":["all"]},{"bearer":["all"]},{"cookie":[]},{}],"summary":"Upload a media file.","tags":["Uploads"]}},"/v1/waitlist":{"post":{"description":"Join the list for one email when something opens. No account needed. Open to our own pages and to programs; a request from a page on another site is refused. `website` is a decoy field for bots: leave it empty. The answer is the same whether or not the address was on the list already, and also for an address that took itself off with its removal link (that join stores nothing). Nobody proves an address is theirs when they join, so when the server sends mail (`confirm_by_email`), an address that has not confirmed gets one email with a link to confirm it and a link to leave, and the launch email goes only to addresses that confirmed. At most one such email goes to a mailbox and list in 24 hours, whatever the caller does (box+1@ and box+2@ are one mailbox), and the whole site sends at most 30 an hour: past that the join still works and only the email waits. At most 5 joins an hour per visitor (an IPv4 address or an IPv6 /64), and 200 an hour for the whole site.","operationId":"joinWaitlist","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistRequest"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistJoined"}}},"description":"Done."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"bad_origin"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request: the email or the interest is not valid (`error.field` says which)."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"summary":"Join the waitlist.","tags":["Waitlist"]}},"/v1/waitlist/confirm":{"get":{"description":"The link in the confirmation email. It carries a signed token that ends after 7 days and names one row of the list, and it changes nothing: mail scanners fetch every link in a message with a GET. The browser is sent (303) to `/waitlist/confirm#token=...`, a page of the site with one button, or to `/waitlist/invalid` for a token that is forged, expired, for a row that is gone, or not a confirmation token. The token travels in the fragment, which no server receives. HEAD is answered the same way. Not for programs: nothing here is JSON.","operationId":"confirmWaitlist","parameters":[{"description":"The signed token from the link in the email.","in":"query","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"303":{"description":"The page with the confirm button, or the page for a link that does not work.","headers":{"Location":{"schema":{"type":"string"}}}}},"summary":"Open the confirmation page for a waitlist address.","tags":["Waitlist"]},"post":{"description":"Confirm an address: what the button on the confirmation page sends. `{ token }` in the body, or `?token=` in the URL. Sets the time the address was confirmed (the first time is kept) and answers `{ ok: true }`, again too. A token that does not work is a 400 `invalid_link` that never says why. Same origin rule as joining (a page of another site is refused; a program sends no Origin).","operationId":"confirmWaitlistButton","parameters":[{"description":"The signed token, when it is not in the body: the one-click unsubscribe of RFC 8058 posts a form to the URL of the mail's `List-Unsubscribe` header, and that URL carries the token.","in":"query","name":"token","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}},"description":"Done."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_link: the token is forged, cut, expired, for a row that is gone, or for the other purpose. It never says which."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"bad_origin"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"summary":"Open the confirmation page for a waitlist address. (the button)","tags":["Waitlist"]}},"/v1/waitlist/leave":{"post":{"description":"Take an address off every list. No account needed, same origin rule and limits as joining. The answer is the same whether or not the address was on a list.","operationId":"leaveWaitlist","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistLeaveRequest"}}},"description":"JSON"},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}},"description":"Done."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"bad_origin"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request: the email is not valid."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"summary":"Leave the waitlist.","tags":["Waitlist"]}},"/v1/waitlist/remove":{"get":{"description":"The removal link in the confirmation email. It carries a signed token, with no end, that names one row of the list, and it changes nothing: mail scanners fetch every link in a message with a GET. The browser is sent (303) to `/waitlist/remove#token=...`, a page of the site with one button, or, for a token that is forged or not a removal token, to `/waitlist/invalid`. HEAD is answered the same way. Not for programs: nothing here is JSON.","operationId":"removeFromWaitlist","parameters":[{"description":"The signed token from the link in the email.","in":"query","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"303":{"description":"The page with the removal button, or the page for a link that does not work.","headers":{"Location":{"schema":{"type":"string"}}}}},"summary":"Open the removal page for a waitlist address.","tags":["Waitlist"]},"post":{"description":"Take one address off one list: what the button on the removal page sends, and the RFC 8058 one-click unsubscribe (the confirmation email has `List-Unsubscribe` and `List-Unsubscribe-Post: List-Unsubscribe=One-Click` headers that name this URL, so a mail client POSTs the form `List-Unsubscribe=One-Click` to it). `{ token }` in the body, or `?token=` in the URL. Removes the row and holds the mailbox for 180 days: a join of it, on any list, stores nothing and sends no email, and answers as ever. Answers `{ ok: true }`, also when the row was gone already. A token that does not work is a 400 `invalid_link`. Same origin rule as joining (a mail provider sends no Origin).","operationId":"removeFromWaitlistButton","parameters":[{"description":"The signed token, when it is not in the body: the one-click unsubscribe of RFC 8058 posts a form to the URL of the mail's `List-Unsubscribe` header, and that URL carries the token.","in":"query","name":"token","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"}}},"description":"Done."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_link: the token is forged, cut, expired, for a row that is gone, or for the other purpose. It never says which."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"bad_origin"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"summary":"Open the removal page for a waitlist address. (the button)","tags":["Waitlist"]}},"/v1/youtube-captions/preview":{"post":{"operationId":"previewYouTubeCaptions","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Discover and quote raw YouTube captions.","tags":["YouTube captions"],"description":"Requires an account. Accepts video, playlist, channel and multiple links. Returns ordered selectable metadata, discovery errors/truncation, cached/new counts and maximum cost. No video/audio downloads or model work.","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"language":{"description":"Original caption language preference; no translation.","type":"string"},"urls":{"items":{"maxLength":2048,"type":"string"},"maxItems":1000,"minItems":1,"type":"array"},"limit":{"maximum":1000,"minimum":1,"type":"integer"}},"required":["urls"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"limits":{"properties":{"acquisition":{"properties":{"reached":{"type":"boolean"},"resets_at":{"description":"Next midnight UTC in epoch milliseconds.","type":"integer"}},"required":["reached","resets_at"],"type":"object"},"max_batch_items":{"type":"integer"},"max_items":{"type":"integer"}},"required":["acquisition","max_batch_items","max_items"],"type":"object"}},"type":"object"}}},"description":"Selection and maximum quote."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}},"/v1/youtube-captions/batches":{"post":{"operationId":"createYouTubeCaptionBatch","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Queue selected videos for raw caption retrieval.","tags":["YouTube captions"],"description":"Submit the selected individual video URLs from preview. One new successfully delivered caption transcript uses one caption credit regardless of duration; failed/partial/canceled/cached/already-delivered results use zero. Caption reservations are atomic and separate from media minutes. Daily retrieval limits produce retryable RATE_LIMIT item errors with no caption charge; resume after midnight UTC. Maximum 1,000 selected videos; two batches/account.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":200,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"language":{"description":"Original caption language preference; no translation.","type":"string"},"urls":{"items":{"maxLength":2048,"type":"string"},"maxItems":1000,"minItems":1,"type":"array"}},"required":["urls"],"type":"object"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"properties":{"batch_id":{"type":"string"},"caption_credits_charged":{"type":"integer"},"counts":{"description":"Total and per-status item counts.","type":"object"},"created_at":{"type":"integer"},"estimate":{"description":"Maximum caption credits including successful deliveries and remaining reservations. One credit per new success; minutes are zero.","type":"object"},"expires_at":{"type":"integer"},"items":{"items":{"description":"Ordered item metadata/status/error, has_result, cache_hit, caption_credits_charged, minutes_charged; transcript bodies are exported separately.","type":"object"},"type":"array"},"language":{"type":"string"},"minutes_charged":{"const":0},"next_cursor":{"type":"string"},"status":{"enum":["queued","running","completed","canceled"]}},"required":["batch_id","status","counts","items","minutes_charged","estimate","expires_at"],"type":"object"}}},"description":"Queued or idempotently replayed batch."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not enough unreserved caption credits."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid selection or reused idempotency key."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}},"/v1/youtube-captions/batches/{id}":{"delete":{"operationId":"cancelYouTubeCaptionBatch","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Cancel unresolved caption items.","tags":["YouTube captions"],"description":"Retains completed results and their charges; releases all unresolved reservations. Does not erase delivered results.","parameters":[{"description":"The owner-scoped caption batch id.","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"batch_id":{"type":"string"},"caption_credits_charged":{"type":"integer"},"counts":{"description":"Total and per-status item counts.","type":"object"},"created_at":{"type":"integer"},"estimate":{"description":"Maximum caption credits including successful deliveries and remaining reservations. One credit per new success; minutes are zero.","type":"object"},"expires_at":{"type":"integer"},"items":{"items":{"description":"Ordered item metadata/status/error, has_result, cache_hit, caption_credits_charged, minutes_charged; transcript bodies are exported separately.","type":"object"},"type":"array"},"language":{"type":"string"},"minutes_charged":{"const":0},"next_cursor":{"type":"string"},"status":{"enum":["queued","running","completed","canceled"]}},"required":["batch_id","status","counts","items","minutes_charged","estimate","expires_at"],"type":"object"}}},"description":"Canceled batch snapshot."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}},"get":{"operationId":"getYouTubeCaptionBatch","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Read private paged caption progress.","tags":["YouTube captions"],"parameters":[{"description":"The owner-scoped caption batch id.","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Next item index returned as next_cursor.","in":"query","name":"cursor","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"maximum":100,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"batch_id":{"type":"string"},"caption_credits_charged":{"type":"integer"},"counts":{"description":"Total and per-status item counts.","type":"object"},"created_at":{"type":"integer"},"estimate":{"description":"Maximum caption credits including successful deliveries and remaining reservations. One credit per new success; minutes are zero.","type":"object"},"expires_at":{"type":"integer"},"items":{"items":{"description":"Ordered item metadata/status/error, has_result, cache_hit, caption_credits_charged, minutes_charged; transcript bodies are exported separately.","type":"object"},"type":"array"},"language":{"type":"string"},"minutes_charged":{"const":0},"next_cursor":{"type":"string"},"status":{"enum":["queued","running","completed","canceled"]}},"required":["batch_id","status","counts","items","minutes_charged","estimate","expires_at"],"type":"object"}}},"description":"Batch snapshot."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}},"/v1/youtube-captions/batches/{id}/resume":{"post":{"operationId":"resumeYouTubeCaptionBatch","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Retry unfinished retrievable items.","tags":["YouTube captions"],"description":"Completed/canceled batches only. Retries retryable failures, partial items and canceled items; successful items are retained and never charged again. indexes narrows selection. A fresh quote reserves only unpaid items.","parameters":[{"description":"The owner-scoped caption batch id.","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"indexes":{"items":{"maximum":999,"minimum":0,"type":"integer"},"maxItems":1000,"type":"array"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"batch_id":{"type":"string"},"caption_credits_charged":{"type":"integer"},"counts":{"description":"Total and per-status item counts.","type":"object"},"created_at":{"type":"integer"},"estimate":{"description":"Maximum caption credits including successful deliveries and remaining reservations. One credit per new success; minutes are zero.","type":"object"},"expires_at":{"type":"integer"},"items":{"items":{"description":"Ordered item metadata/status/error, has_result, cache_hit, caption_credits_charged, minutes_charged; transcript bodies are exported separately.","type":"object"},"type":"array"},"language":{"type":"string"},"minutes_charged":{"const":0},"next_cursor":{"type":"string"},"status":{"enum":["queued","running","completed","canceled"]}},"required":["batch_id","status","counts","items","minutes_charged","estimate","expires_at"],"type":"object"}}},"description":"Resumed snapshot."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not enough unreserved caption credits."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Batch is still running."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}},"/v1/youtube-captions/batches/{id}/items/{index}/result":{"get":{"operationId":"getYouTubeCaptionResult","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Read one private canonical raw caption result.","tags":["YouTube captions"],"description":"Returns the stored VideoContext without upstream calls or charges. Owner, expiry and takedown checks apply.","parameters":[{"description":"The owner-scoped caption batch id.","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"path","name":"index","required":true,"schema":{"maximum":999,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Context"}}},"description":"Raw caption context."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Result is not ready."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}},"/v1/youtube-captions/batches/{id}/export":{"get":{"operationId":"exportYouTubeCaptionBatch","security":[{"apiKey":[]},{"bearer":[]},{"cookie":[]}],"summary":"Stream ordered private caption exports.","tags":["YouTube captions"],"description":"TXT/Markdown combined output, ZIP with per-video TXT/SRT and JSON/CSV manifest, or JSON/CSV manifest only. index selects one completed item; SRT/VTT require index. Rechecks ownership, expiry and takedowns while streaming. Exporting is free.","parameters":[{"description":"The owner-scoped caption batch id.","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"query","name":"format","schema":{"default":"zip","enum":["zip","md","txt","csv","json","srt","vtt"]}},{"in":"query","name":"index","schema":{"maximum":999,"minimum":0,"type":"integer"}}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"description":"Private streamed attachment."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Selected item has no completed result."},"410":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request refused."}}}}},"servers":[{"description":"The one public base for every client: https://scribiz.com/api (Better Auth at /api/auth, remote MCP at https://scribiz.com/mcp). The proxy routes both paths to this API.","url":"https://scribiz.com/api"}],"tags":[{"name":"Jobs"},{"name":"YouTube captions"},{"name":"Uploads"},{"name":"Account"},{"name":"Billing"},{"name":"Waitlist"},{"name":"Contact"},{"name":"Auth"},{"name":"MCP"},{"name":"Gemini proxy"},{"name":"Service"}]}