{"openapi":"3.1.0","info":{"title":"Storeshot API","version":"1.11.0","description":"The Storeshot public API. Generate App Store and Google Play marketing screenshots.\n\n## Conventions\n- JSON fields are camelCase; enum values are lower snake_case; error codes are UPPER_SNAKE.\n- Collections are returned as `{ \"data\": [...] }`.\n- Every response carries an `X-Request-Id` header. Send your own (1–128 chars of `A-Z a-z 0-9 _ - =`) to correlate logs.\n- Every error uses the `ErrorEnvelope`: `{ \"error\": { \"code\", \"message\", \"requestId\", \"docUrl\", \"details?\" } }`.\n\nError code reference: `/v1/docs/errors`.\n\n## Authentication\nSend `Authorization: Bearer <credential>`, where the credential is an API key (`ss_live_…` or `ss_test_…`) or a Draggable Auth access token. Never put credentials in the URL. Test keys (`ss_test_…`) never incur charges. Each operation lists the scope an API key needs; access tokens have every scope.\n\n## Client identification\nOptionally send `X-Storeshot-Client: <surface>/<name>`, where surface is `api`, `web`, `mcp` or `cli` and name is 1–100 characters of `A-Z a-z 0-9 . _ @ + -` (for example `cli/storeshot-cli@0.3.1`). It labels your usage (`GET /usage?groupBy=source`); it is not authentication. Without it, usage is labelled `api` and your `User-Agent` is recorded.\n\n## Rate limits\nAuthenticated requests are counted per minute in two buckets: per API key (`key`, 60 requests) and per account (`user`, 120 requests, shared by all API keys and access tokens of the account; access tokens use only this bucket). Test keys count like live keys. A request is counted only if every bucket allows it; a refused request counts nowhere. The numbers here are the current defaults and may differ per plan: `RateLimit-Policy` is authoritative.\n\nEvery authenticated response carries `RateLimit-Policy` (each policy as `\"name\";q=<requests>;w=<seconds>`) and `RateLimit` (the most restrictive policy as `\"name\";r=<remaining>;t=<seconds until reset>`), following draft-ietf-httpapi-ratelimit-headers. Over the limit you get `429 RATE_LIMITED` with `Retry-After` and `details.scope`.\n\nBilled generations are also capped per account: 3 in progress at once (`429 CONCURRENCY_LIMITED`, retry after 10 s) and 100 per UTC day (`429 RATE_LIMITED`, `details.policy` `daily_generations`, retry after the next UTC midnight). `/health`, `/devices`, this document and the docs are never rate limited. Browsers can read these headers and `X-Request-Id` (CORS).\n\n## Uploads and retention\nHand an image over once and reference its `id` in generations. Three ways: `POST /uploads` with `multipart/form-data` (one part `file`); `POST /uploads/presign`, then `PUT` the file to the returned URL from your server and call `POST /uploads/{id}/complete`; or `POST /uploads` with `{ \"url\": \"https://…\" }` to have the server fetch it. Each way ends in the same `ready` upload with the same metadata.\n\nAccepted: PNG, JPEG and WebP, detected from the bytes (never from the file name or declared type), at most 20 MB and 50 megapixels (16384 pixels per edge). HEIC/HEIF is not supported. An account can hold 500 uploads or 2 GB (pending and ready uploads count; deleting frees space). Test keys may upload too (same limits).\n\nURL imports fetch `https://` URLs on port 443 only, and only from public addresses (private, loopback, link-local and other special-purpose addresses are refused, also after a redirect), with at most 3 redirects and 15 seconds in total. Only the origin and path of the URL are stored.\n\n**Retention:** uploads, with their files, are deleted 30 days after creation (pending or failed uploads after 24 hours); you can delete one earlier with `DELETE /uploads/{id}`. Generated results are kept until you delete them (results of test keys: 30 days).\n\n## Generations\n`POST /generations` turns one screenshot into one marketing image of exactly the size a store requires (a `GET /devices` id, or its `width`×`height`). It returns at once with `202` and a generation in `queued`; a worker moves it to `running`, then to `succeeded` (with `result.image.url`), `failed` (with `error.code`) or `canceled`. Retries after a temporary provider error happen inside `running`.\n\n**Waiting.** Poll `GET /generations/{id}` (every 2–5 seconds is plenty), or send `Prefer: wait=<seconds>` (or `?wait=<seconds>`, at most 60) on `POST /generations` or `GET /generations/{id}` to hold the response until the generation is final: `200` when it is, `202` when the time ran out, with `Preference-Applied: wait=<seconds>` when the wait was honoured. A busy server may answer `202` at once without `Preference-Applied`; keep polling then.\n\n**Idempotency.** Send `Idempotency-Key: <1–255 printable ASCII characters>` with every `POST /generations`. Repeating a request with the same key and body returns the original generation (with `Idempotent-Replayed: true`) and never creates or charges a second one; the same key with a different body, or from a credential of the other environment, is `409 IDEMPOTENCY_CONFLICT`. Keys are kept at least as long as the generation exists. A refused request (4xx) stores nothing, so you can retry it with the same key.\n\n**Credits.** A generation with a live key costs 1 credit, charged when it is created (`creditsCharged`). If it fails, or you cancel it before it starts (`POST /generations/{id}/cancel`), the credit is refunded automatically (`creditsRefunded`). Without enough credits you get `402 INSUFFICIENT_CREDITS` with `details.topUpUrl`; nothing is created. Live generations also count towards the concurrency and daily caps (see Rate limits).\n\n**Test keys.** With an `ss_test_…` key everything works the same way (validation, uploads of the test environment, queue, statuses, idempotency, result URLs and delete), but no credits are used and no image model runs: the result is a sample image of exactly the requested size, and a missing caption becomes `Sample caption (test mode)`. Test generations do not count towards the generation caps.\n\n**Results and retention.** Result URLs are signed and expire after 15 minutes (`result.urlsExpireAt`); `GET` the generation again for fresh ones. Results are kept until you `DELETE /generations/{id}` (results of test keys are deleted 30 days after they finish); a deleted generation keeps its record with `deletedAt` set. Input uploads expire after 30 days, so a generation queued from an expired upload fails with `UPLOAD_NOT_READY` and is refunded.\n\n### Quick start\ncurl:\n```sh\ncurl -s -X POST https://api.storeshot.art/v1/uploads -H \"Authorization: Bearer $STORESHOT_KEY\" -F file=@home.png\ncurl -s -X POST \"https://api.storeshot.art/v1/generations\" -H \"Authorization: Bearer $STORESHOT_KEY\" \\\n  -H \"Content-Type: application/json\" -H \"Idempotency-Key: home-en-1\" -H \"Prefer: wait=60\" \\\n  -d '{\"inputs\":[{\"uploadId\":\"<upload id>\"}],\"store\":\"app_store\",\"device\":\"app_store_1290x2796\",\"app\":{\"name\":\"Trailmate\"}}'\n```\nNode (18+, ES module):\n```js\nimport { readFile } from 'node:fs/promises';\nconst api = 'https://api.storeshot.art/v1';\nconst headers = { Authorization: `Bearer ${process.env.STORESHOT_KEY}` };\nconst form = new FormData();\nform.append('file', new Blob([await readFile('home.png')]), 'home.png');\nconst upload = await (await fetch(`${api}/uploads`, { method: 'POST', headers, body: form })).json();\nlet gen = await (await fetch(`${api}/generations`, {\n  method: 'POST',\n  headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': 'home-en-1' },\n  body: JSON.stringify({ inputs: [{ uploadId: upload.id }], store: 'app_store', device: 'app_store_1290x2796', app: { name: 'Trailmate' } }),\n})).json();\nwhile (gen.status === 'queued' || gen.status === 'running') {\n  await new Promise((r) => setTimeout(r, 3000));\n  gen = await (await fetch(`${api}/generations/${gen.id}`, { headers })).json();\n}\nconsole.log(gen.status, gen.result?.image.url ?? gen.error);\n```\nPython (requests):\n```python\nimport os, time, requests\napi, h = \"https://api.storeshot.art/v1\", {\"Authorization\": f\"Bearer {os.environ['STORESHOT_KEY']}\"}\nupload = requests.post(f\"{api}/uploads\", headers=h, files={\"file\": open(\"home.png\", \"rb\")}).json()\ngen = requests.post(f\"{api}/generations\", headers={**h, \"Idempotency-Key\": \"home-en-1\", \"Prefer\": \"wait=60\"},\n    json={\"inputs\": [{\"uploadId\": upload[\"id\"]}], \"store\": \"app_store\", \"device\": \"app_store_1290x2796\", \"app\": {\"name\": \"Trailmate\"}}).json()\nwhile gen[\"status\"] in (\"queued\", \"running\"):\n    time.sleep(3); gen = requests.get(f\"{api}/generations/{gen['id']}\", headers=h).json()\nprint(gen[\"status\"], (gen[\"result\"] or {}).get(\"image\", {}).get(\"url\") or gen[\"error\"])\n```\n\n## Screenshot sets\n`POST /screenshot-sets` turns 1–10 screenshots into a whole store listing in one call: every screen on every device of one or both stores, plus an optional Google Play feature graphic (`featureGraphic: true`), with one caption per screen and one palette and logo for all. Each image is a normal generation (listed by `GET /generations?screenshotSetId=<id>`), so statuses, refunds and test keys work exactly as above.\n\n**Devices.** Without `devices`, a store gets the sizes its required screenshot groups ask for: the App Store `app_store_1290x2796` (6.9\") and `app_store_1179x2556` (6.3\"), Google Play `google_play_1080x1920`. Pass `devices` to choose fewer (or others, such as iPad or tablet sizes): only sizes that meet the store rules (`GET /stores/{store}/requirements`) are accepted. A set has at most 50 images.\n\n**Credits.** With a live key every image costs 1 credit, so a set costs `screens × devices (+1 for the feature graphic)`. Ask first with `?dryRun=true`: the quote (`images`, `credits`, `availableCredits`, `sufficient`) validates the request exactly like a create but creates and charges nothing. The create charges all images at once or none (`402 INSUFFICIENT_CREDITS` with `details.requiredCredits`); the `202` response shows `creditsCharged`. A failed or canceled image is refunded; `POST /screenshot-sets/{id}/retry` creates new images for the failed ones only (1 credit each, again all or none). Repeating a retry never charges twice. A set counts as one generation towards the concurrency cap and all its images towards the daily cap.\n\n**Status and download.** `GET /screenshot-sets/{id}` reports `queued`, `running`, `succeeded`, `partially_failed`, `failed` or `canceled`, with `counts` and every image. `GET /screenshot-sets/{id}/archive` streams a ZIP of the succeeded images plus `manifest.json` (`complete: false` lists what is missing): `<store>/<device>/<NN>.png`, or with `?layout=fastlane` the folders fastlane `deliver` (`fastlane/screenshots/en-US/`) and `supply` (`fastlane/metadata/android/en-US/images/`) read. `supply` replaces the images already in your Play listing.\n\n### Quick start\ncurl:\n```sh\ncurl -s -X POST \"https://api.storeshot.art/v1/screenshot-sets?dryRun=true\" -H \"Authorization: Bearer $STORESHOT_KEY\" \\\n  -H \"Content-Type: application/json\" -d @set.json\ncurl -s -X POST https://api.storeshot.art/v1/screenshot-sets -H \"Authorization: Bearer $STORESHOT_KEY\" \\\n  -H \"Content-Type: application/json\" -H \"Idempotency-Key: release-2.4\" -d @set.json\ncurl -s -o listing.zip \"https://api.storeshot.art/v1/screenshot-sets/<id>/archive?layout=fastlane\" -H \"Authorization: Bearer $STORESHOT_KEY\"\n```\nwith `set.json` `{\"app\":{\"name\":\"Trailmate\"},\"screens\":[{\"input\":{\"uploadId\":\"<upload id>\"},\"caption\":\"Find every trail\"}],\"stores\":[{\"store\":\"app_store\"},{\"store\":\"google_play\",\"featureGraphic\":true}]}`.\nNode (18+, ES module):\n```js\nimport { writeFile } from 'node:fs/promises';\nconst api = 'https://api.storeshot.art/v1';\nconst headers = { Authorization: `Bearer ${process.env.STORESHOT_KEY}`, 'Content-Type': 'application/json' };\nconst body = JSON.stringify({ app: { name: 'Trailmate' }, screens: [{ input: { uploadId: process.env.UPLOAD_ID }, caption: 'Find every trail' }], stores: [{ store: 'app_store' }] });\nconst quote = await (await fetch(`${api}/screenshot-sets?dryRun=true`, { method: 'POST', headers, body })).json();\nconsole.log(`${quote.images} images, ${quote.credits} credits`);\nlet set = await (await fetch(`${api}/screenshot-sets`, { method: 'POST', headers: { ...headers, 'Idempotency-Key': 'release-2.4' }, body })).json();\nwhile (set.status === 'queued' || set.status === 'running') {\n  await new Promise((r) => setTimeout(r, 5000));\n  set = await (await fetch(`${api}/screenshot-sets/${set.id}`, { headers })).json();\n}\nconst zip = await fetch(`${api}/screenshot-sets/${set.id}/archive`, { headers });\nawait writeFile('listing.zip', Buffer.from(await zip.arrayBuffer()));\n```\nPython (requests):\n```python\nimport os, time, requests\napi, h = \"https://api.storeshot.art/v1\", {\"Authorization\": f\"Bearer {os.environ['STORESHOT_KEY']}\"}\nbody = {\"app\": {\"name\": \"Trailmate\"}, \"screens\": [{\"input\": {\"uploadId\": os.environ[\"UPLOAD_ID\"]}, \"caption\": \"Find every trail\"}], \"stores\": [{\"store\": \"app_store\"}]}\nprint(requests.post(f\"{api}/screenshot-sets\", params={\"dryRun\": \"true\"}, headers=h, json=body).json()[\"credits\"])\ns = requests.post(f\"{api}/screenshot-sets\", headers={**h, \"Idempotency-Key\": \"release-2.4\"}, json=body).json()\nwhile s[\"status\"] in (\"queued\", \"running\"):\n    time.sleep(5); s = requests.get(f\"{api}/screenshot-sets/{s['id']}\", headers=h).json()\nopen(\"listing.zip\", \"wb\").write(requests.get(f\"{api}/screenshot-sets/{s['id']}/archive\", headers=h).content)\n```\n\n## Webhooks\nInstead of polling, register an `https://` endpoint with `POST /webhooks` (scope `webhooks:write`, not granted by default) and receive a signed `POST` when a generation finishes (`generation.succeeded`, `generation.failed` incl. canceled), a screenshot set finishes (`screenshot_set.completed`) or the live balance falls below `creditsLowThreshold` (`credits.low`). A webhook belongs to one environment: test-key generations and sets reach test webhooks only. `POST /webhooks/{id}/test` sends a `webhook.test` event; `GET /webhooks/{id}/deliveries` shows every attempt. The event bodies are documented under `webhooks` in this document.\n\n**Delivery.** At least once, unordered, from `StoreshotWebhooks/1.0`. Any 2xx within 10 seconds acknowledges; redirects are not followed. Failures are retried after about 1 min, 5 min, 30 min, 1 h, 3 h, 6 h and 12 h (8 attempts, ~23 h), then the delivery is `exhausted`. Destinations must stay public: every attempt resolves the host again and refuses private addresses. Deduplicate on `Storeshot-Event-Id`. Generation payloads carry no download URLs: `GET /generations/{id}` for fresh ones.\n\n**Signatures.** `Storeshot-Signature: t=<unix seconds>,v1=<hex>` where `v1` = HMAC-SHA256(secret, `<t>.<raw body>`). Compute it over the exact bytes received, compare in constant time, accept any `v1` (two during a secret rotation grace) and reject a `t` more than 5 minutes away. The secret (`whsec_…`) is shown only by create and `POST /webhooks/{id}/rotate-secret`. Verification code for Node and Python: `docs/api/webhooks.md`.\n\n**Limits.** 10 webhooks per environment, 10 test events per webhook per hour. URLs are shown masked (`https://<host>/…<last 4 path characters>`); delivery logs are kept 30 days.\n\n## Billing\nBuy one-time credit packs: `GET /billing/packs` lists them (empty while web payments are switched off), `POST /billing/checkout` with a `pack` returns a hosted checkout `url` for the buyer, and `GET /billing/checkouts/{id}` reports `open`, `processing`, `paid`, `partially_refunded`, `refunded`, `expired` or `failed`. Purchased credits are added once the payment succeeds and never expire; the payment provider is the merchant of record and applies taxes at checkout. A refund takes back the refunded share of the credits as far as they are unused. Scopes `billing:read` and `billing:write` are not granted by default; checkouts need a live credential. At most 10 checkouts per account per hour.\n\n## Helpers\nFree building blocks for agents (no credits; counted by the request rate limits only). `POST /palettes` extracts a brand palette from an upload or an `https://` URL with the same local algorithm as the Storeshot app (`extract`), or the dominant colour of up to 10 images (`dominant`); no AI model is involved and URL inputs are never stored. `GET /stores` and `GET /stores/{store}/requirements` describe what the App Store and Google Play need (formats, counts, sizes or dimension rules, required classes, the feature graphic), with the official pages they come from and which `GET /devices` sizes meet them."},"servers":[{"url":"https://api.storeshot.art/v1","description":"Production"}],"tags":[{"name":"System","description":"Service status."},{"name":"Catalog","description":"Static reference data."},{"name":"Account","description":"The authenticated caller."},{"name":"API keys","description":"Create, list and revoke API keys."},{"name":"Usage","description":"Credit balance and generation usage."},{"name":"Uploads","description":"Hand over images once; reference them in generations."},{"name":"Generations","description":"Turn a screenshot into a store-ready marketing image."},{"name":"Screenshot sets","description":"A whole store listing (every screen on every device) in one call."},{"name":"Palettes","description":"Free brand palettes from your images."},{"name":"Webhooks","description":"Signed HTTPS callbacks when generations and sets finish."},{"name":"Billing","description":"Buy credit packs (hosted checkout)."}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"ss_live_… / ss_test_…","description":"Storeshot API key. Create one with POST /api-keys; the secret is shown once."},"userToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Draggable Auth access token (console and apps). Holds every scope."}},"schemas":{"ApiKeyEnvironment":{"type":"string","enum":["live","test"],"description":"`test` keys never incur charges or call image providers."},"GenerationStatus":{"type":"string","enum":["queued","running","succeeded","failed","canceled"],"description":"`queued`: waiting for a worker (can be canceled). `running`: being generated (retries keep it `running`). `succeeded`, `failed` and `canceled` are final."},"StoreTarget":{"type":"string","enum":["app_store","google_play"]},"Orientation":{"type":"string","enum":["portrait","landscape"]},"PaletteColor":{"type":"object","properties":{"hex":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","example":"#0F766E"},"name":{"type":"string","minLength":1,"maxLength":40,"example":"Teal"}},"required":["hex","name"],"additionalProperties":false},"WebhookGenerationData":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/GenerationStatus"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"inputs":{"type":"array","items":{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid"},"sourceUrl":{"type":["string","null"],"description":"For a URL input: origin and path of the URL (it was imported as `uploadId`)."}},"required":["uploadId","sourceUrl"]}},"store":{"$ref":"#/components/schemas/StoreTarget"},"device":{"type":"string","description":"Catalogue id of the size.","example":"app_store_1290x2796"},"size":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["width","height"]},"orientation":{"$ref":"#/components/schemas/Orientation"},"app":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name","description"]},"caption":{"type":["string","null"],"description":"As requested; null = derive."},"palette":{"type":["array","null"],"items":{"$ref":"#/components/schemas/PaletteColor"},"description":"As requested; null = extract from the screenshot."},"logo":{"type":["object","null"],"properties":{"uploadId":{"type":"string","format":"uuid"}},"required":["uploadId"]},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"creditsCharged":{"type":"integer","minimum":0,"description":"1 for live keys, 0 for test keys."},"creditsRefunded":{"type":"integer","minimum":0,"description":"Refunded when the generation failed or was canceled before it started."},"result":{"type":["object","null"],"properties":{"image":{"type":"object","properties":{"contentType":{"type":"string","enum":["image/png","image/jpeg"],"description":"PNG; JPEG for the Google Play feature graphic."},"width":{"type":"integer","exclusiveMinimum":0,"example":1290},"height":{"type":"integer","exclusiveMinimum":0,"example":2796}},"required":["contentType","width","height"]},"thumbnail":{"type":["object","null"],"properties":{"contentType":{"type":"string","enum":["image/webp"]}},"required":["contentType"]},"caption":{"type":"string","description":"The caption used: yours, or the one derived from the screenshot (may be empty)."},"palette":{"type":"array","items":{"$ref":"#/components/schemas/PaletteColor"},"description":"The colours used."}},"required":["image","thumbnail","caption","palette"],"description":"As in `GET /generations/{id}` but without URLs (they expire): fetch the generation for fresh ones."},"error":{"type":["object","null"],"properties":{"code":{"type":"string","enum":["GENERATION_FAILED","GENERATION_TIMEOUT","NOT_A_SCREENSHOT","UPLOAD_NOT_READY","INTERNAL_ERROR"]},"message":{"type":"string"}},"required":["code","message"],"description":"Only when `failed`. See /v1/docs/errors for each code."},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"startedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"finishedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"deletedAt":{"type":["string","null"],"description":"Set by `DELETE`: the result files are gone, this record stays.","example":"2026-10-21T12:00:00.000Z"},"screenshotSet":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"position":{"type":"integer","minimum":1,"description":"The screen (1-based)."},"attempt":{"type":"integer","minimum":1,"description":"2 and up: a retry."}},"required":["id","position","attempt"],"description":"Present only when this generation is an image of a screenshot set (`POST /screenshot-sets`)."}},"required":["id","status","environment","inputs","store","device","size","orientation","app","caption","palette","logo","metadata","creditsCharged","creditsRefunded","result","error","createdAt","startedAt","finishedAt","deletedAt"],"description":"The `Generation` at the time of the event, without download URLs."},"GenerationSucceededEvent":{"type":"object","properties":{"id":{"type":"string","description":"Event id (`evt_…`), identical on every attempt: deduplicate on it."},"type":{"type":"string","enum":["generation.succeeded"]},"created":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"apiVersion":{"type":"string","example":"1.8.0"},"data":{"$ref":"#/components/schemas/WebhookGenerationData"}},"required":["id","type","created","environment","apiVersion","data"]},"GenerationFailedEvent":{"type":"object","properties":{"id":{"type":"string","description":"Event id (`evt_…`), identical on every attempt: deduplicate on it."},"type":{"type":"string","enum":["generation.failed"]},"created":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"apiVersion":{"type":"string","example":"1.8.0"},"data":{"$ref":"#/components/schemas/WebhookGenerationData"}},"required":["id","type","created","environment","apiVersion","data"]},"ScreenshotSetStatus":{"type":"string","enum":["queued","running","succeeded","partially_failed","failed","canceled"],"description":"Computed from the current image of every slot: `queued` (all queued), `running` (any running, or some finished and some not), `succeeded` (all succeeded), `partially_failed` (all finished, some succeeded), `canceled` (none succeeded, all canceled), `failed` (none succeeded, otherwise)."},"ScreenshotSet":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/ScreenshotSetStatus"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"app":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name","description"]},"orientation":{"$ref":"#/components/schemas/Orientation"},"palette":{"type":"array","items":{"$ref":"#/components/schemas/PaletteColor"}},"paletteSource":{"type":"string","enum":["given","auto"],"description":"`auto`: extracted from screen 1 when the set was created."},"logo":{"type":["object","null"],"properties":{"uploadId":{"type":"string","format":"uuid"}},"required":["uploadId"]},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"screens":{"type":"array","items":{"type":"object","properties":{"position":{"type":"integer","minimum":1},"input":{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid"}},"required":["uploadId"]},"caption":{"type":"string"}},"required":["position","input","caption"]}},"stores":{"type":"array","items":{"type":"object","properties":{"store":{"$ref":"#/components/schemas/StoreTarget"},"devices":{"type":"array","items":{"type":"string"}},"featureGraphic":{"type":["object","null"],"properties":{"screen":{"type":"integer","minimum":1},"caption":{"type":"string"}},"required":["screen","caption"]}},"required":["store","devices","featureGraphic"]}},"images":{"type":"integer","exclusiveMinimum":0},"counts":{"type":"object","properties":{"queued":{"type":"integer","minimum":0},"running":{"type":"integer","minimum":0},"succeeded":{"type":"integer","minimum":0},"failed":{"type":"integer","minimum":0},"canceled":{"type":"integer","minimum":0}},"required":["queued","running","succeeded","failed","canceled"]},"creditsCharged":{"type":"integer","minimum":0,"description":"Every image of the set, retries included (0 with a test key)."},"creditsRefunded":{"type":"integer","minimum":0},"children":{"type":"array","items":{"type":"object","properties":{"position":{"type":"integer","minimum":1,"description":"The screen (1-based)."},"store":{"$ref":"#/components/schemas/StoreTarget"},"device":{"type":"string"},"size":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["width","height"]},"featureGraphic":{"type":"boolean"},"generationId":{"type":"string","format":"uuid"},"attempt":{"type":"integer","minimum":1,"maximum":5},"status":{"$ref":"#/components/schemas/GenerationStatus"},"error":{"type":["object","null"],"properties":{"code":{"type":"string","enum":["GENERATION_FAILED","GENERATION_TIMEOUT","NOT_A_SCREENSHOT","UPLOAD_NOT_READY","INTERNAL_ERROR"]},"message":{"type":"string"}},"required":["code","message"]}},"required":["position","store","device","size","featureGraphic","generationId","attempt","status","error"]},"description":"The current image of every slot (store, device, screen). Result URLs: `GET /generations?screenshotSetId=` or the archive."},"archiveUrl":{"type":"string","example":"/v1/screenshot-sets/5c1e…/archive"},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"updatedAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"finishedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"deletedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","status","environment","app","orientation","palette","paletteSource","logo","metadata","screens","stores","images","counts","creditsCharged","creditsRefunded","children","archiveUrl","createdAt","updatedAt","finishedAt","deletedAt"]},"ScreenshotSetCompletedEvent":{"type":"object","properties":{"id":{"type":"string","description":"Event id (`evt_…`), identical on every attempt: deduplicate on it."},"type":{"type":"string","enum":["screenshot_set.completed"]},"created":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"apiVersion":{"type":"string","example":"1.8.0"},"data":{"$ref":"#/components/schemas/ScreenshotSet"}},"required":["id","type","created","environment","apiVersion","data"]},"CreditsLowEvent":{"type":"object","properties":{"id":{"type":"string","description":"Event id (`evt_…`), identical on every attempt: deduplicate on it."},"type":{"type":"string","enum":["credits.low"]},"created":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"apiVersion":{"type":"string","example":"1.8.0"},"data":{"type":"object","properties":{"balances":{"type":"object","properties":{"starter":{"type":"integer"},"subscription":{"type":"integer"},"purchased":{"type":"integer"}},"required":["starter","subscription","purchased"]},"total":{"type":"integer"},"threshold":{"type":"integer"}},"required":["balances","total","threshold"]}},"required":["id","type","created","environment","apiVersion","data"]},"WebhookTestEvent":{"type":"object","properties":{"id":{"type":"string","description":"Event id (`evt_…`), identical on every attempt: deduplicate on it."},"type":{"type":"string","enum":["webhook.test"]},"created":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"apiVersion":{"type":"string","example":"1.8.0"},"data":{"type":"object","properties":{"webhookId":{"type":"string","format":"uuid"},"message":{"type":"string"}},"required":["webhookId","message"]}},"required":["id","type","created","environment","apiVersion","data"]},"CheckStatus":{"type":"string","enum":["up","down","not_configured"]},"HealthReport":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","degraded"]},"version":{"type":"string","description":"Server release version."},"commit":{"type":"string","description":"Deployed git commit (short SHA)."},"checks":{"type":"object","properties":{"database":{"$ref":"#/components/schemas/CheckStatus"},"redis":{"$ref":"#/components/schemas/CheckStatus"}},"required":["database","redis"]}},"required":["status","version","commit","checks"]},"ErrorCode":{"type":"string","enum":["INVALID_INPUT","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","METHOD_NOT_ALLOWED","PAYLOAD_TOO_LARGE","UPLOAD_NOT_READY","IDEMPOTENCY_CONFLICT","GENERATION_STATE_CONFLICT","SCREENSHOT_SET_STATE_CONFLICT","WEBHOOK_STATE_CONFLICT","BILLING_STATE_CONFLICT","IMAGE_TOO_LARGE","UNSUPPORTED_FORMAT","NOT_A_SCREENSHOT","URL_FETCH_FAILED","WEBHOOK_URL_REJECTED","INSUFFICIENT_CREDITS","RATE_LIMITED","CONCURRENCY_LIMITED","INTERNAL_ERROR","GENERATION_FAILED","SERVICE_UNAVAILABLE","BILLING_UNAVAILABLE","GENERATION_TIMEOUT"],"description":"Stable, machine-readable error code. See /v1/docs/errors."},"ErrorEnvelope":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable message; safe to display."},"requestId":{"type":"string","description":"Equals the X-Request-Id response header."},"docUrl":{"type":"string","format":"uri","description":"Documentation for this error code."},"details":{"type":"object","additionalProperties":{},"description":"Code-specific structured details."}},"required":["code","message","requestId","docUrl"]}},"required":["error"]},"DeviceKind":{"type":"string","enum":["screenshot","feature_graphic"]},"Device":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier: `<store>_<width>x<height>`.","example":"app_store_1284x2778"},"store":{"$ref":"#/components/schemas/StoreTarget"},"kind":{"$ref":"#/components/schemas/DeviceKind"},"label":{"type":"string","description":"Display name; may change. Do not parse."},"deviceHint":{"type":"string","description":"Example devices; may change. Do not parse."},"width":{"type":"integer","exclusiveMinimum":0,"description":"Pixels. Portrait (canonical) for screenshots; landscape swaps width/height."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Pixels."},"orientations":{"type":"array","items":{"$ref":"#/components/schemas/Orientation"},"minItems":1}},"required":["id","store","kind","label","deviceHint","width","height","orientations"]},"DeviceList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Device"}}},"required":["data"]},"ApiKeyScope":{"type":"string","enum":["generations:write","generations:read","uploads:write","usage:read","keys:manage","billing:read","billing:write","webhooks:read","webhooks:write"],"description":"Permission an API key carries. Access tokens implicitly hold every scope."},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"displayName":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"email":{"type":"string","description":"Only when authenticated with an access token."},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","displayName","avatarUrl","createdAt"]},"credential":{"type":"object","properties":{"type":{"type":"string","enum":["user_token","api_key"]},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyScope"}},"apiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string"},"expiresAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","name","prefix","expiresAt"],"description":"Only when authenticated with an API key. Never the secret."}},"required":["type","environment","scopes"]}},"required":["user","credential"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string","description":"First characters of the key, for display.","example":"ss_live_AbCd"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyScope"}},"status":{"type":"string","enum":["active","expired"]},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"lastUsedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"expiresAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","name","prefix","environment","scopes","status","createdAt","lastUsedAt","expiresAt"]},"CreatedApiKey":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":"string","description":"The full secret. Returned only in this response; store it now."}},"required":["key"]}]},"CreateApiKeyRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[^\\p{Cc}]+$","description":"1–64 characters after trimming; need not be unique."},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyScope"},"minItems":1,"maxItems":9,"description":"Defaults to every scope except `keys:manage`, `billing:read`, `billing:write`, `webhooks:read` and `webhooks:write`."},"expiresAt":{"type":"string","format":"date-time","description":"Optional RFC 3339 expiry, in the future and at most 365 days ahead."}},"required":["name","environment"],"additionalProperties":false},"ApiKeyList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["data"],"description":"Non-revoked keys, newest first (at most 100)."},"CreditBalances":{"type":"object","properties":{"balances":{"type":"object","properties":{"starter":{"type":"integer","minimum":0},"subscription":{"type":"integer","minimum":0},"purchased":{"type":"integer","minimum":0}},"required":["starter","subscription","purchased"]},"total":{"type":"integer","minimum":0,"description":"Sum of the three balances."}},"required":["balances","total"],"description":"Remaining credits. A caller with no credits reads all zeros.","example":{"balances":{"starter":0,"subscription":0,"purchased":4},"total":4}},"UsageGroupBy":{"type":"string","enum":["day","key","source"],"description":"`day`: one bucket per UTC day in the range, including empty days. `key`: per API key id (`none` for calls without a key). `source`: per surface (`mobile`, `api`, `web`, `mcp`, `cli`)."},"UsageCounts":{"type":"object","properties":{"generations":{"type":"integer","minimum":0,"description":"Generations started in the bucket."},"succeeded":{"type":"integer","minimum":0,"description":"Completed."},"failed":{"type":"integer","minimum":0,"description":"Failed before any credit was charged."},"refunded":{"type":"integer","minimum":0,"description":"Charged, then refunded."},"pending":{"type":"integer","minimum":0,"description":"Still running."},"creditsUsed":{"type":"integer","minimum":0,"description":"Credits charged minus credits refunded for these generations. One generation can use several credits."}},"required":["generations","succeeded","failed","refunded","pending","creditsUsed"]},"UsageBucket":{"allOf":[{"$ref":"#/components/schemas/UsageCounts"},{"type":"object","properties":{"key":{"type":"string","description":"The UTC day, API key id or source."}},"required":["key"]}]},"UsageReport":{"type":"object","properties":{"from":{"type":"string","description":"Resolved start (inclusive).","example":"2026-10-21T12:00:00.000Z"},"to":{"type":"string","description":"Resolved end (exclusive).","example":"2026-10-21T12:00:00.000Z"},"groupBy":{"$ref":"#/components/schemas/UsageGroupBy"},"data":{"type":"array","items":{"$ref":"#/components/schemas/UsageBucket"},"description":"Buckets sorted by `key`."},"totals":{"$ref":"#/components/schemas/UsageCounts"}},"required":["from","to","groupBy","data","totals"],"description":"Usage of the whole account (every key and surface), bucketed by when each generation started. Refunds always count against the generation they belong to.","example":{"from":"2026-10-20T00:00:00.000Z","to":"2026-10-21T14:03:12.000Z","groupBy":"day","data":[{"key":"2026-10-20","generations":0,"succeeded":0,"failed":0,"refunded":0,"pending":0,"creditsUsed":0},{"key":"2026-10-21","generations":3,"succeeded":2,"failed":0,"refunded":1,"pending":0,"creditsUsed":2}],"totals":{"generations":3,"succeeded":2,"failed":0,"refunded":1,"pending":0,"creditsUsed":2}}},"UploadStatus":{"type":"string","enum":["pending","ready","failed","deleted"],"description":"`pending`: presigned, waiting for the file and `complete`. `ready`: usable in generations. `failed`: the file was rejected (it is deleted). `deleted`: deleted by you or by retention (metadata only)."},"UploadOrientation":{"type":["string","null"],"enum":["portrait","landscape","square"],"description":"From the EXIF-oriented size; `square` when equal."},"Upload":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/UploadStatus"},"source":{"type":"string","enum":["multipart","presign","url"],"description":"How the file arrived: a multipart upload, a presigned PUT, or a URL import."},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"contentType":{"type":"string","enum":["image/png","image/jpeg","image/webp"],"description":"Detected from the file bytes (for a pending presigned upload: the declared type)."},"bytes":{"type":"integer","exclusiveMinimum":0,"description":"File size (declared, while pending)."},"width":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Pixels, EXIF-oriented. Null unless ready."},"height":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Pixels, EXIF-oriented. Null unless ready."},"orientation":{"$ref":"#/components/schemas/UploadOrientation"},"sha256":{"type":["string","null"],"description":"Hex SHA-256 of the file. Null unless ready."},"sourceUrl":{"type":["string","null"],"description":"URL imports only: origin and path of the URL you sent (query and fragment are never stored)."},"expiresAt":{"type":"string","description":"When the upload and its file are deleted: 30 days after creation (24 hours for pending or failed uploads). Generated results are kept until you delete them.","example":"2026-10-21T12:00:00.000Z"},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","status","source","environment","contentType","bytes","width","height","orientation","sha256","sourceUrl","expiresAt","createdAt"],"example":{"id":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a","status":"ready","source":"multipart","environment":"live","contentType":"image/png","bytes":482113,"width":1290,"height":2796,"orientation":"portrait","sha256":"9b1e5e5a3c0f8d2b7a6c4e1f0d9b8a7c6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b","sourceUrl":null,"expiresAt":"2026-11-20T10:00:00.000Z","createdAt":"2026-10-21T10:00:00.000Z"}},"CreateUploadFromUrl":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"description":"An `https://` URL (port 443, no credentials, at most 2048 characters) that resolves to a public address. Fetched once, server-side.","example":"https://example.com/screenshots/home.png"}},"required":["url"],"additionalProperties":false},"PresignedUpload":{"type":"object","properties":{"upload":{"$ref":"#/components/schemas/Upload"},"uploadUrl":{"type":"string","format":"uri","description":"PUT the file here, from your server."},"method":{"type":"string","enum":["PUT"]},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Send exactly these headers with the PUT (they are signed).","example":{"Content-Type":"image/png","Content-Length":"482113"}},"uploadUrlExpiresAt":{"type":"string","description":"15 minutes after this response.","example":"2026-10-21T12:00:00.000Z"}},"required":["upload","uploadUrl","method","headers","uploadUrlExpiresAt"]},"PresignUploadRequest":{"type":"object","properties":{"contentType":{"type":"string","minLength":1,"maxLength":100,"description":"The type of the file you will PUT: `image/png`, `image/jpeg` or `image/webp`.","example":"image/png"},"bytes":{"type":"integer","exclusiveMinimum":0,"description":"Exact size of the file you will PUT, at most 20 MB (20971520).","example":482113}},"required":["contentType","bytes"],"additionalProperties":false},"UploadList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Upload"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; null on the last page."}},"required":["data","nextCursor"],"description":"Uploads that are not deleted, newest first."},"GenerationResult":{"type":["object","null"],"properties":{"image":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Signed download URL (expires at `urlsExpireAt`)."},"contentType":{"type":"string","enum":["image/png","image/jpeg"],"description":"PNG; JPEG for the Google Play feature graphic."},"width":{"type":"integer","exclusiveMinimum":0,"example":1290},"height":{"type":"integer","exclusiveMinimum":0,"example":2796}},"required":["url","contentType","width","height"]},"thumbnail":{"type":["object","null"],"properties":{"url":{"type":"string","format":"uri"},"contentType":{"type":"string","enum":["image/webp"]}},"required":["url","contentType"],"description":"A 512 px preview. Best effort: its URL may return 404."},"urlsExpireAt":{"type":"string","description":"15 minutes after this response. Fetch the generation again for fresh URLs.","example":"2026-10-21T12:00:00.000Z"},"caption":{"type":"string","description":"The caption used: yours, or the one derived from the screenshot (may be empty)."},"palette":{"type":"array","items":{"$ref":"#/components/schemas/PaletteColor"},"description":"The colours used."}},"required":["image","thumbnail","urlsExpireAt","caption","palette"],"description":"Only when `succeeded` and not deleted."},"Generation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/GenerationStatus"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"inputs":{"type":"array","items":{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid"},"sourceUrl":{"type":["string","null"],"description":"For a URL input: origin and path of the URL (it was imported as `uploadId`)."}},"required":["uploadId","sourceUrl"]}},"store":{"$ref":"#/components/schemas/StoreTarget"},"device":{"type":"string","description":"Catalogue id of the size.","example":"app_store_1290x2796"},"size":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["width","height"]},"orientation":{"$ref":"#/components/schemas/Orientation"},"app":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name","description"]},"caption":{"type":["string","null"],"description":"As requested; null = derive."},"palette":{"type":["array","null"],"items":{"$ref":"#/components/schemas/PaletteColor"},"description":"As requested; null = extract from the screenshot."},"logo":{"type":["object","null"],"properties":{"uploadId":{"type":"string","format":"uuid"}},"required":["uploadId"]},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"creditsCharged":{"type":"integer","minimum":0,"description":"1 for live keys, 0 for test keys."},"creditsRefunded":{"type":"integer","minimum":0,"description":"Refunded when the generation failed or was canceled before it started."},"result":{"$ref":"#/components/schemas/GenerationResult"},"error":{"type":["object","null"],"properties":{"code":{"type":"string","enum":["GENERATION_FAILED","GENERATION_TIMEOUT","NOT_A_SCREENSHOT","UPLOAD_NOT_READY","INTERNAL_ERROR"]},"message":{"type":"string"}},"required":["code","message"],"description":"Only when `failed`. See /v1/docs/errors for each code."},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"startedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"finishedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"deletedAt":{"type":["string","null"],"description":"Set by `DELETE`: the result files are gone, this record stays.","example":"2026-10-21T12:00:00.000Z"},"screenshotSet":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"position":{"type":"integer","minimum":1,"description":"The screen (1-based)."},"attempt":{"type":"integer","minimum":1,"description":"2 and up: a retry."}},"required":["id","position","attempt"],"description":"Present only when this generation is an image of a screenshot set (`POST /screenshot-sets`)."}},"required":["id","status","environment","inputs","store","device","size","orientation","app","caption","palette","logo","metadata","creditsCharged","creditsRefunded","result","error","createdAt","startedAt","finishedAt","deletedAt"],"example":{"id":"0b8e7a52-41c6-4f38-9d0e-7f6a1c2b3d4e","status":"succeeded","environment":"live","inputs":[{"uploadId":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a","sourceUrl":null}],"store":"app_store","device":"app_store_1290x2796","size":{"width":1290,"height":2796},"orientation":"portrait","app":{"name":"Trailmate","description":"Offline hiking maps"},"caption":"Find every trail, even offline","palette":[{"hex":"#0F766E","name":"Teal"}],"logo":null,"metadata":{"campaign":"spring","locale":"en-US"},"creditsCharged":1,"creditsRefunded":0,"result":{"image":{"url":"https://storage.example.com/platform/users/5b0c7d2e-1f3a-4c5b-8d6e-7f8091a2b3c4/generations/job-9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d-a1.png?X-Amz-Expires=900&X-Amz-Signature=abc123","contentType":"image/png","width":1290,"height":2796},"thumbnail":{"url":"https://storage.example.com/platform/users/5b0c7d2e-1f3a-4c5b-8d6e-7f8091a2b3c4/generations/job-9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d-a1.png.thumb.webp?X-Amz-Expires=900&X-Amz-Signature=def456","contentType":"image/webp"},"urlsExpireAt":"2026-10-22T10:15:00.000Z","caption":"Find every trail, even offline","palette":[{"hex":"#0F766E","name":"Teal"}]},"error":null,"createdAt":"2026-10-22T10:00:00.000Z","startedAt":"2026-10-22T10:00:01.000Z","finishedAt":"2026-10-22T10:00:24.000Z","deletedAt":null}},"GenerationInput":{"anyOf":[{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid","description":"A `ready` upload of the same environment (test or live) as the credential."}},"required":["uploadId"],"additionalProperties":false},{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048,"description":"An `https://` URL, fetched once and stored as an upload (same rules as `POST /uploads`)."}},"required":["url"],"additionalProperties":false}],"description":"An image: an upload id or an https URL."},"GenerationSize":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0,"example":1290},"height":{"type":"integer","exclusiveMinimum":0,"example":2796}},"required":["width","height"],"additionalProperties":false,"description":"Output pixels: a catalogue size of the store (portrait, or swapped for landscape; the Google Play feature graphic is 1024×500 only)."},"GenerationMetadata":{"type":"object","additionalProperties":{"type":"string","maxLength":500},"description":"Your own labels, echoed back and filterable (`GET /generations?metadata=key:value`): at most 20 keys of 1–40 characters (`A-Z a-z 0-9 _ . -`), string values of at most 500 characters, 4 KB in total.","example":{"campaign":"spring","locale":"en-US"}},"CreateGenerationRequest":{"type":"object","properties":{"inputs":{"type":"array","items":{"$ref":"#/components/schemas/GenerationInput"},"minItems":1,"maxItems":1,"description":"Exactly one screenshot in this version."},"store":{"$ref":"#/components/schemas/StoreTarget"},"device":{"type":"string","minLength":1,"maxLength":64,"description":"A `GET /devices` id of the same store. Exactly one of `device` and `size` is required.","example":"app_store_1290x2796"},"size":{"$ref":"#/components/schemas/GenerationSize"},"orientation":{"allOf":[{"$ref":"#/components/schemas/Orientation"},{"description":"Default: from `size` (wider than tall is landscape), else portrait (the feature graphic: landscape). Must agree with the size and the device."}]},"app":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"Trailmate"},"description":{"type":"string","maxLength":2000,"default":"","example":"Offline hiking maps"}},"required":["name"],"additionalProperties":false},"caption":{"type":"string","maxLength":1000,"description":"The marketing caption. Absent or blank: derived from the screenshot.","example":"Find every trail, even offline"},"palette":{"type":"array","items":{"$ref":"#/components/schemas/PaletteColor"},"minItems":1,"maxItems":8,"description":"1–8 colours. Absent: extracted from the screenshot."},"logo":{"allOf":[{"$ref":"#/components/schemas/GenerationInput"},{"description":"Your app icon or logo (any shape; PNG, JPEG or WebP)."}]},"metadata":{"$ref":"#/components/schemas/GenerationMetadata"}},"required":["inputs","store","app"],"additionalProperties":false,"example":{"inputs":[{"uploadId":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"}],"store":"app_store","device":"app_store_1290x2796","orientation":"portrait","app":{"name":"Trailmate","description":"Offline hiking maps"},"caption":"Find every trail, even offline","palette":[{"hex":"#0F766E","name":"Teal"}],"metadata":{"campaign":"spring","locale":"en-US"}}},"GenerationList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Generation"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; null on the last page."}},"required":["data","nextCursor"],"description":"Generations that are not deleted, newest first."},"ScreenshotSetQuote":{"type":"object","properties":{"dryRun":{"type":"boolean","enum":[true]},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"images":{"type":"integer","exclusiveMinimum":0},"credits":{"type":"integer","minimum":0,"description":"What creating this set charges now (1 per image with a live key, 0 with a test key)."},"availableCredits":{"type":"integer","minimum":0},"sufficient":{"type":"boolean"},"screens":{"type":"integer","exclusiveMinimum":0},"breakdown":{"type":"array","items":{"type":"object","properties":{"store":{"$ref":"#/components/schemas/StoreTarget"},"device":{"type":"string","example":"app_store_1290x2796"},"size":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["width","height"]},"orientation":{"$ref":"#/components/schemas/Orientation"},"images":{"type":"integer","exclusiveMinimum":0},"defaulted":{"type":"boolean","description":"The store default (no `devices` given)."}},"required":["store","device","size","orientation","images","defaulted"]}}},"required":["dryRun","environment","images","credits","availableCredits","sufficient","screens","breakdown"],"description":"Nothing was created or charged. A quote is not a reservation."},"SetUploadRef":{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid","description":"A `ready` upload of the same environment (test or live) as the credential."}},"required":["uploadId"],"additionalProperties":false,"description":"An upload id (URL inputs are not accepted in sets)."},"ScreenshotSetScreen":{"type":"object","properties":{"input":{"$ref":"#/components/schemas/SetUploadRef"},"caption":{"type":"string","minLength":1,"maxLength":1000,"description":"Required (1–1000 characters): the same caption is used on every device of this screen.","example":"Find every trail"}},"required":["input","caption"],"additionalProperties":false},"ScreenshotSetFeatureGraphic":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{"screen":{"type":"integer","minimum":1,"maximum":10,"description":"Position (1-based) of the screen used as the source."},"caption":{"type":"string","minLength":1,"maxLength":1000,"description":"Default: the caption of that screen."}},"required":["screen"],"additionalProperties":false}],"description":"Google Play only. `true`: one 1024×500 feature graphic from screen 1 with its caption (+1 image)."},"ScreenshotSetTarget":{"type":"object","properties":{"store":{"$ref":"#/components/schemas/StoreTarget"},"devices":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":5,"description":"`GET /devices` screenshot ids of this store that meet its rules (`GET /stores/{store}/requirements`). Absent: the store defaults (App Store: `app_store_1290x2796` and `app_store_1179x2556`, the 6.9\" and 6.3\" iPhone groups Apple asks for; Google Play: `google_play_1080x1920`). Pass fewer devices to pay for fewer images.","example":["google_play_1080x1920","google_play_1600x2560"]},"featureGraphic":{"$ref":"#/components/schemas/ScreenshotSetFeatureGraphic"}},"required":["store"],"additionalProperties":false},"CreateScreenshotSetRequest":{"type":"object","properties":{"app":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"example":"Trailmate"},"description":{"type":"string","maxLength":2000,"default":"","example":"Offline hiking maps"}},"required":["name"],"additionalProperties":false},"screens":{"type":"array","items":{"$ref":"#/components/schemas/ScreenshotSetScreen"},"minItems":1,"maxItems":10,"description":"1–10 screenshots, in listing order."},"stores":{"type":"array","items":{"$ref":"#/components/schemas/ScreenshotSetTarget"},"minItems":1,"maxItems":2},"orientation":{"allOf":[{"$ref":"#/components/schemas/Orientation"},{"default":"portrait","description":"Of the screenshots (the feature graphic is always landscape)."}]},"palette":{"type":"array","items":{"$ref":"#/components/schemas/PaletteColor"},"minItems":1,"maxItems":8,"description":"1–8 colours. Absent: extracted once from screen 1 and used for every image."},"logo":{"allOf":[{"$ref":"#/components/schemas/SetUploadRef"},{"description":"Your app icon or logo, used on every image."}]},"metadata":{"$ref":"#/components/schemas/GenerationMetadata"}},"required":["app","screens","stores"],"additionalProperties":false,"example":{"app":{"name":"Trailmate","description":"Offline hiking maps"},"screens":[{"input":{"uploadId":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"},"caption":"Find every trail"},{"input":{"uploadId":"7a2d3c4b-5e6f-4a7b-8c9d-0e1f2a3b4c5d"},"caption":"Works offline"}],"stores":[{"store":"app_store"},{"store":"google_play","devices":["google_play_1080x1920","google_play_1600x2560"],"featureGraphic":true}],"orientation":"portrait","palette":[{"hex":"#0F766E","name":"Teal"}],"metadata":{"release":"2.4"}}},"ScreenshotSetList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScreenshotSet"}},"nextCursor":{"type":["string","null"]}},"required":["data","nextCursor"],"description":"Sets that are not deleted, newest first."},"ScreenshotSetRetryResult":{"type":"object","properties":{"created":{"type":"array","items":{"type":"object","properties":{"generationId":{"type":"string","format":"uuid"},"position":{"type":"integer","minimum":1},"device":{"type":"string"},"attempt":{"type":"integer","minimum":2,"maximum":5}},"required":["generationId","position","device","attempt"]}},"skipped":{"type":"array","items":{"type":"object","properties":{"generationId":{"type":"string","format":"uuid"},"reason":{"type":"string","enum":["superseded","not_failed","attempts_exhausted"]}},"required":["generationId","reason"]}}},"required":["created","skipped"]},"RetriedScreenshotSet":{"allOf":[{"$ref":"#/components/schemas/ScreenshotSet"},{"type":"object","properties":{"retry":{"$ref":"#/components/schemas/ScreenshotSetRetryResult"}},"required":["retry"]}]},"RetryScreenshotSetRequest":{"type":"object","properties":{"generationIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":50,"description":"Images of this set to retry. Absent: every failed or canceled image."}},"additionalProperties":false},"ExtractedColor":{"type":"object","properties":{"hex":{"type":"string","pattern":"^#[0-9A-F]{6}$","description":"Uppercase `#RRGGBB`.","example":"#2040A0"},"name":{"type":"string","description":"Nearest named colour.","example":"Steel Blue"},"inputIndex":{"type":"integer","minimum":0,"description":"`dominant` only: index in `inputs` of the image this colour came from."}},"required":["hex","name"]},"Palette":{"type":"object","properties":{"mode":{"type":"string","enum":["extract","dominant"]},"source":{"type":"string","enum":["extracted","default"],"description":"`default`: no colour could be extracted, so `colors` is the Storeshot default palette."},"colors":{"type":"array","items":{"$ref":"#/components/schemas/ExtractedColor"},"minItems":1}},"required":["mode","source","colors"],"example":{"mode":"extract","source":"extracted","colors":[{"hex":"#2040A0","name":"Steel Blue"},{"hex":"#E0E0E0","name":"Silver"}]}},"PaletteInput":{"anyOf":[{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid","description":"A `ready` upload of the same environment (test or live) as the credential."}},"required":["uploadId"],"additionalProperties":false},{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048,"description":"An `https://` URL (same rules as `POST /uploads`), fetched for this request only: nothing is stored."}},"required":["url"],"additionalProperties":false}],"description":"An image: an upload id or an https URL."},"CreatePaletteRequest":{"type":"object","properties":{"inputs":{"type":"array","items":{"$ref":"#/components/schemas/PaletteInput"},"minItems":1,"maxItems":10,"description":"`extract`: exactly one image. `dominant`: 1–10 images. At most 3 URL inputs."},"mode":{"type":"string","enum":["extract","dominant"],"default":"extract","description":"`extract`: up to `maxColors` distinct colours of one image (the Storeshot app’s palette). `dominant`: the dominant colour of each image."},"maxColors":{"type":"integer","minimum":1,"maximum":10,"description":"`extract` only: 1–10, default 5.","example":5}},"required":["inputs"],"additionalProperties":false,"example":{"inputs":[{"uploadId":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"}],"mode":"extract","maxColors":5}},"StoreSummary":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/StoreTarget"},"name":{"type":"string"},"requirementsUrl":{"type":"string","example":"/v1/stores/app_store/requirements"}},"required":["id","name","requirementsUrl"]},"StoreList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StoreSummary"}}},"required":["data"]},"StoreRuleIssue":{"type":"string","enum":["size_not_accepted","edge_below_min","edge_above_max","aspect_ratio_exceeds_2_1","aspect_ratio_not_exact"]},"StoreDeviceClass":{"type":"object","properties":{"id":{"type":"string","example":"phone"},"family":{"type":"string","enum":["iphone","ipad","watch","phone","tablet","chromebook","xr"]},"label":{"type":"string"},"requirement":{"type":"string","enum":["required","conditional","optional"],"description":"`conditional`: named by a `requiredGroups` entry (one of its classes is needed when its condition holds); `optional`: never needed."},"count":{"type":"object","properties":{"min":{"type":"integer","minimum":0},"max":{"type":"integer","exclusiveMinimum":0}},"required":["min","max"],"description":"Screenshots per locale when this class is provided."},"maxFileBytes":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Per-class file limit; null when the store publishes none."},"acceptedSizes":{"type":["array","null"],"items":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["width","height"]},"description":"App Store: every accepted pixel size (both orientations). Null when `rules` apply."},"rules":{"type":["object","null"],"properties":{"minEdge":{"type":"integer","exclusiveMinimum":0,"description":"Shorter side, at least."},"maxEdge":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Longer side, at most (null: no published maximum)."},"maxAspectRatio":{"type":"number","description":"Longer side ÷ shorter side, at most."},"exactAspectRatio":{"type":["string","null"],"description":"`long:short` the image must match exactly, or null."},"recommendedAspectRatios":{"type":"array","items":{"type":"string"},"description":"Advisory (store promotion), not enforced."}},"required":["minEdge","maxEdge","maxAspectRatio","exactAspectRatio","recommendedAspectRatios"],"description":"Google Play: dimension rules. Null when `acceptedSizes` apply."},"sources":{"type":"array","items":{"type":"string","enum":["apple_screenshot_specs","play_preview_assets"]}},"devices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"google_play_1080x1920"},"compliant":{"type":"boolean"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/StoreRuleIssue"}}},"required":["id","compliant","issues"]},"description":"`GET /devices` sizes of this class. `compliant: false` lists the hard store rules the size breaks (in portrait or landscape)."}},"required":["id","family","label","requirement","count","maxFileBytes","acceptedSizes","rules","sources","devices"]},"StoreRequirements":{"type":"object","properties":{"store":{"$ref":"#/components/schemas/StoreTarget"},"name":{"type":"string"},"screenshots":{"type":"object","properties":{"formats":{"type":"array","items":{"type":"string","enum":["image/png","image/jpeg"]}},"alpha":{"type":"boolean","description":"false: no transparency (no alpha channel)."},"maxFileBytes":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Null when the store publishes no limit (see a class’s own `maxFileBytes`)."},"count":{"type":"object","properties":{"minTotal":{"type":"integer","minimum":0,"description":"Across all classes."},"maxPerDeviceClass":{"type":"integer","exclusiveMinimum":0}},"required":["minTotal","maxPerDeviceClass"]},"sources":{"type":"array","items":{"type":"string","enum":["apple_screenshot_specs","play_preview_assets"]}}},"required":["formats","alpha","maxFileBytes","count","sources"]},"deviceClasses":{"type":"array","items":{"$ref":"#/components/schemas/StoreDeviceClass"}},"requiredGroups":{"type":"array","items":{"type":"object","properties":{"when":{"type":"string","example":"app runs on iPad"},"anyOf":{"type":"array","items":{"type":"string"},"description":"Provide at least one of these classes."},"sources":{"type":"array","items":{"type":"string","enum":["apple_screenshot_specs","play_preview_assets"]}}},"required":["when","anyOf","sources"]}},"featureGraphic":{"type":["object","null"],"properties":{"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0},"formats":{"type":"array","items":{"type":"string","enum":["image/png","image/jpeg"]}},"alpha":{"type":"boolean"},"maxFileBytes":{"type":["integer","null"],"exclusiveMinimum":0},"required":{"type":"boolean"},"sources":{"type":"array","items":{"type":"string","enum":["apple_screenshot_specs","play_preview_assets"]}},"device":{"type":"string","example":"google_play_1024x500"}},"required":["width","height","formats","alpha","maxFileBytes","required","sources","device"],"description":"Google Play only."},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["apple_screenshot_specs","play_preview_assets"]},"title":{"type":"string"},"url":{"type":"string","format":"uri"},"checkedOn":{"type":"string","description":"Date the facts were last checked (UTC)."}},"required":["id","title","url","checkedOn"]}}},"required":["store","name","screenshots","deviceClasses","requiredGroups","featureGraphic","sources"],"description":"What a store needs from screenshots, from the official pages in `sources` (checked on `checkedOn`). Stores change their rules; treat this as guidance."},"WebhookEventType":{"type":"string","enum":["generation.succeeded","generation.failed","screenshot_set.completed","credits.low"],"description":"`generation.succeeded`; `generation.failed` (a generation ended `failed` or `canceled`: see `data.status`); `screenshot_set.completed` (a set reached a final status, again after a retry); `credits.low` (live only: the balance fell below `creditsLowThreshold`). `webhook.test` is sent on demand by `POST /webhooks/{id}/test` and needs no subscription."},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"environment":{"$ref":"#/components/schemas/ApiKeyEnvironment"},"url":{"type":"string","description":"Masked: scheme, host and the last 4 characters of the path.","example":"https://hooks.example.com/…shot"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"description":{"type":"string"},"enabled":{"type":"boolean"},"creditsLowThreshold":{"type":"integer"},"secret":{"type":"object","properties":{"hint":{"type":"string","description":"The last 4 characters.","example":"whsec_…Xy7Q"},"rotatedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"previousExpiresAt":{"type":["string","null"],"description":"Until then deliveries are also signed with the previous secret.","example":"2026-10-21T12:00:00.000Z"}},"required":["hint","rotatedAt","previousExpiresAt"]},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"updatedAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"}},"required":["id","environment","url","events","description","enabled","creditsLowThreshold","secret","createdAt","updatedAt"]},"WebhookWithSecret":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"object","properties":{"hint":{"type":"string","description":"The last 4 characters.","example":"whsec_…Xy7Q"},"rotatedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"previousExpiresAt":{"type":["string","null"],"description":"Until then deliveries are also signed with the previous secret.","example":"2026-10-21T12:00:00.000Z"},"value":{"type":"string","description":"The signing secret (`whsec_…`). Returned only in this response; store it now. Verify `Storeshot-Signature` with it."}},"required":["hint","rotatedAt","previousExpiresAt","value"]}}}]},"CreateWebhookRequest":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048,"description":"An `https://` URL on port 443, without credentials, at most 2048 characters, whose host resolves only to public addresses (checked now and before every delivery). Shown masked afterwards.","example":"https://hooks.example.com/storeshot"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"maxItems":4,"description":"The business events to deliver (1–4, unique)."},"description":{"type":"string","maxLength":200,"pattern":"^[^\\p{Cc}]*$","description":"Free text, at most 200 characters."},"enabled":{"type":"boolean","description":"Default `true`."},"creditsLowThreshold":{"type":"integer","minimum":1,"maximum":10000,"description":"Default 5."},"environment":{"allOf":[{"$ref":"#/components/schemas/ApiKeyEnvironment"},{"description":"Which events the webhook receives: `live` or `test` (test-key generations and sets). Default: the API key environment (access tokens: `live`). An API key can only create webhooks of its own environment."}]}},"required":["url","events"],"additionalProperties":false},"WebhookList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}},"required":["data"],"description":"Webhooks, newest first (at most 10 per environment)."},"UpdateWebhookRequest":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048,"description":"An `https://` URL on port 443, without credentials, at most 2048 characters, whose host resolves only to public addresses (checked now and before every delivery). Shown masked afterwards.","example":"https://hooks.example.com/storeshot"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"maxItems":4,"description":"The business events to deliver (1–4, unique)."},"description":{"type":"string","maxLength":200,"pattern":"^[^\\p{Cc}]*$","description":"Free text, at most 200 characters."},"enabled":{"type":"boolean"},"creditsLowThreshold":{"type":"integer","minimum":1,"maximum":10000,"description":"`credits.low` fires when the live balance falls below this many credits (1–10000)."}},"additionalProperties":false},"RotateWebhookSecretRequest":{"type":"object","properties":{"gracePeriodSeconds":{"type":"integer","minimum":0,"maximum":604800,"description":"How long the previous secret keeps signing deliveries next to the new one (0–604800, default 86400). 0 drops it at once."}},"additionalProperties":false},"WebhookTestResult":{"type":"object","properties":{"eventId":{"type":"string","example":"evt_3f2a…"},"deliveryId":{"type":"string","format":"uuid"}},"required":["eventId","deliveryId"]},"WebhookDeliveryStatus":{"type":"string","enum":["pending","succeeded","exhausted","failed","canceled"],"description":"`pending`: queued or retrying. `succeeded`: a 2xx answer. `exhausted`: 8 attempts failed. `failed`: the destination was refused (`blocked_destination`, `invalid_url`) or the secret could not be read. `canceled`: the webhook was disabled before the attempt."},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventId":{"type":"string","example":"evt_3f2a…"},"eventType":{"type":"string","enum":["generation.succeeded","generation.failed","screenshot_set.completed","credits.low","webhook.test"]},"status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attempts":{"type":"integer","minimum":0,"maximum":8},"nextAttemptAt":{"type":["string","null"],"description":"When `pending`: the next attempt is due then (null while one is in flight).","example":"2026-10-21T12:00:00.000Z"},"createdAt":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"finishedAt":{"type":["string","null"],"description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"log":{"type":"array","items":{"type":"object","properties":{"attempt":{"type":"integer","minimum":1,"maximum":8},"at":{"type":"string","description":"RFC 3339 UTC timestamp.","example":"2026-10-21T12:00:00.000Z"},"latencyMs":{"type":"integer","minimum":0},"statusCode":{"type":["integer","null"]},"errorClass":{"type":["string","null"],"enum":["http_status","timeout","connect_failed","tls_error","dns_failure","blocked_destination","redirect","invalid_url","secret_unavailable"]},"responseSnippet":{"type":["string","null"],"description":"First 256 characters of the response body, control characters removed."}},"required":["attempt","at","latencyMs","statusCode","errorClass","responseSnippet"]}}},"required":["id","eventId","eventType","status","attempts","nextAttemptAt","createdAt","finishedAt","log"]},"WebhookDeliveryList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"nextCursor":{"type":["string","null"]}},"required":["data","nextCursor"],"description":"Deliveries of this webhook, newest first."},"BillingPack":{"type":"object","properties":{"id":{"type":"string","description":"Pack id to send as `pack`.","example":"credits_15"},"name":{"type":"string","example":"Storeshot API: 15 credits"},"credits":{"type":"integer","description":"Purchased credits (never expire).","example":15},"priceAmount":{"type":"integer","description":"List price in minor units (cents). Taxes are applied at checkout.","example":399},"currency":{"type":"string","description":"ISO 4217, lowercase.","example":"usd"}},"required":["id","name","credits","priceAmount","currency"]},"BillingPackList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPack"}}},"required":["data"],"description":"Packs on sale, cheapest first. Empty while web payments are switched off."},"BillingCheckout":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["open","processing","paid","partially_refunded","refunded","expired","failed"],"description":"`open` (send the buyer to `url`), `processing` (payment confirmed, credits on the way), `paid` (credits granted), `partially_refunded`, `refunded`, `expired` (not paid in time) or `failed`."},"pack":{"type":"string","example":"credits_15"},"credits":{"type":"integer"},"priceAmount":{"type":"integer","description":"Minor units (cents).","example":399},"currency":{"type":"string","example":"usd"},"url":{"type":["string","null"],"format":"uri","description":"Hosted checkout page (Polar). Null unless `status` is `open`.","example":"https://buy.polar.sh/polar_c_abc123"},"expiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"paidAt":{"type":["string","null"],"format":"date-time"},"creditsGranted":{"type":"integer","description":"Credits added to `purchased`."},"creditsReversed":{"type":"integer","description":"Credits taken back after a refund (spent credits are not taken twice)."},"kind":{"type":"string","enum":["subscription"],"description":"SS-012: present (`subscription`) only on plan checkouts; then `pack` and `plan` hold the plan id and the credits are the monthly allotment, added to `subscription`. Absent on pack checkouts."},"plan":{"type":"string","enum":["pro","agency","scale"],"description":"SS-012: the plan of a plan checkout (absent on pack checkouts)."},"planName":{"type":"string","description":"SS-013: the plan name, on `GET /billing/checkouts/{id}` of a plan checkout only (absent on pack checkouts).","example":"Storeshot API Pro"},"subscriptionStatus":{"type":["string","null"],"description":"SS-013: the payment provider's status of the subscription this plan checkout created (`active`, `trialing`, `past_due`, `canceled`, …), or null while none is linked yet. Only on `GET /billing/checkouts/{id}` of a plan checkout.","example":"active"}},"required":["id","status","pack","credits","priceAmount","currency","url","expiresAt","createdAt","paidAt","creditsGranted","creditsReversed"]},"CreateBillingCheckoutRequest":{"type":"object","properties":{"pack":{"type":"string","minLength":1,"maxLength":40,"example":"credits_15"},"successUrl":{"type":"string","maxLength":2048,"format":"uri","description":"Where the buyer lands after paying. Default: a static confirmation page. `https://` on storeshot.art, a storeshot.art subdomain or this API host; at most 2048 characters."},"returnUrl":{"type":"string","maxLength":2048,"format":"uri","description":"Where \"back\" on the checkout page leads. Default: the credits page. `https://` on storeshot.art, a storeshot.art subdomain or this API host; at most 2048 characters."}},"required":["pack"],"additionalProperties":false},"BillingPlanLimits":{"type":"object","properties":{"requestsPerMinutePerKey":{"type":"integer"},"requestsPerMinutePerUser":{"type":"integer"},"concurrentGenerations":{"type":"integer"},"dailyGenerations":{"type":"integer","description":"Credits per UTC day."},"maxStoredUploads":{"type":"integer"},"maxStoredUploadMegabytes":{"type":"integer"}},"required":["requestsPerMinutePerKey","requestsPerMinutePerUser","concurrentGenerations","dailyGenerations","maxStoredUploads","maxStoredUploadMegabytes"],"description":"The limits that apply on a plan."},"BillingPlan":{"type":"object","properties":{"id":{"type":"string","enum":["pro","agency","scale"],"description":"Plan id to send as `plan`.","example":"pro"},"name":{"type":"string","example":"Storeshot API Pro"},"credits":{"type":"integer","description":"Credits added to `subscription` each paid month. They are used before any other credits and expire (reset) at renewal; pack credits never expire.","example":60},"priceAmount":{"type":"integer","description":"Monthly list price in minor units (cents). Taxes are applied at checkout.","example":1200},"currency":{"type":"string","description":"ISO 4217, lowercase.","example":"usd"},"interval":{"type":"string","enum":["month"]},"limits":{"$ref":"#/components/schemas/BillingPlanLimits"}},"required":["id","name","credits","priceAmount","currency","interval","limits"]},"BillingPlanList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlan"}}},"required":["data"],"description":"Plans on sale, cheapest first. Empty while plans are switched off."},"BillingSubscription":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"plan":{"type":["string","null"],"enum":["pro","agency","scale"],"description":"Null when the subscription moved to a product that is not a Storeshot plan."},"status":{"type":"string","description":"The payment provider status: `active`, `trialing`, `past_due`, `incomplete`, `incomplete_expired`, `unpaid`, `paused` or `canceled`.","example":"active"},"cancelAtPeriodEnd":{"type":"boolean"},"currentPeriodStart":{"type":["string","null"],"format":"date-time"},"currentPeriodEnd":{"type":["string","null"],"format":"date-time"},"pendingPlan":{"type":["string","null"],"enum":["pro","agency","scale"],"description":"A downgrade that takes effect at the next renewal."},"creditsPerPeriod":{"type":"integer"},"subscriptionCredits":{"type":"integer","description":"The `subscription` balance now (expires at renewal)."}},"required":["id","plan","status","cancelAtPeriodEnd","currentPeriodStart","currentPeriodEnd","pendingPlan","creditsPerPeriod","subscriptionCredits"],"description":"The latest subscription of the account, or null."},"BillingSubscriptionState":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro","agency","scale"],"description":"The tier whose limits apply now. A plan applies while its subscription is active (until one day after the period end) or past due (three days after the period end)."},"limits":{"$ref":"#/components/schemas/BillingPlanLimits"},"subscription":{"$ref":"#/components/schemas/BillingSubscription"}},"required":["plan","limits","subscription"]},"CreateSubscriptionCheckoutRequest":{"type":"object","properties":{"plan":{"type":"string","enum":["pro","agency","scale"],"example":"pro"},"successUrl":{"type":"string","maxLength":2048,"format":"uri","description":"Where the buyer lands after paying. Default: a static confirmation page. `https://` on storeshot.art, a storeshot.art subdomain or this API host; at most 2048 characters."},"returnUrl":{"type":"string","maxLength":2048,"format":"uri","description":"Where \"back\" on the checkout page leads. Default: the credits page. `https://` on storeshot.art, a storeshot.art subdomain or this API host; at most 2048 characters."}},"required":["plan"],"additionalProperties":false},"ChangeSubscriptionRequest":{"type":"object","properties":{"plan":{"type":"string","enum":["pro","agency","scale"],"example":"agency"}},"required":["plan"],"additionalProperties":false},"BillingPortalSession":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Open it in the browser of the account owner."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"When a `session` link stops working; null for `email_login`."},"mode":{"type":"string","enum":["session","email_login"],"description":"`session`: signed in as this account. `email_login`: the provider asks for the billing email and sends a one-time code."}},"required":["url","expiresAt","mode"]},"CreatePortalSessionRequest":{"type":"object","properties":{"returnUrl":{"type":"string","maxLength":2048,"format":"uri","description":"Where \"back\" in the portal leads. Default: the credits page. `https://` on storeshot.art, a storeshot.art subdomain or this API host; at most 2048 characters."}},"additionalProperties":false}},"parameters":{}},"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Service health","description":"Reports whether the API and its dependencies are healthy. No authentication.","tags":["System"],"security":[],"responses":{"200":{"description":"All dependencies are healthy.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthReport"}}}},"503":{"description":"A dependency is down (`SERVICE_UNAVAILABLE`; `details` holds the report).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/devices":{"get":{"operationId":"listDevices","summary":"List store screenshot sizes","description":"App Store and Google Play image sizes Storeshot can generate, including the Google Play 1024×500 feature graphic. No authentication.","tags":["Catalog"],"security":[],"parameters":[{"schema":{"allOf":[{"$ref":"#/components/schemas/StoreTarget"},{"description":"Only return sizes for this store."}]},"required":false,"name":"store","in":"query"}],"responses":{"200":{"description":"Device catalogue.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceList"}}}},"400":{"description":"Invalid `store` (`INVALID_INPUT`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/me":{"get":{"operationId":"getMe","summary":"Who am I","description":"Describes the caller and the credential used. Any valid API key or access token; no scope required.","tags":["Account"],"security":[{"apiKey":[]},{"userToken":[]}],"responses":{"200":{"description":"The caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api-keys":{"post":{"operationId":"createApiKey","summary":"Create an API key","description":"Requires `keys:manage`. The full key is in this response only. An API key acting as the manager can only create keys for its own environment with a subset of its own scopes.","tags":["API keys"],"security":[{"apiKey":["keys:manage"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"201":{"description":"Created. Store `key` now; it is never shown again.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiKey"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listApiKeys","summary":"List API keys","description":"Requires `keys:manage`. Non-revoked keys, newest first (max 100). Secrets are never returned. An API key caller sees only keys of its own environment.","tags":["API keys"],"security":[{"apiKey":["keys:manage"]},{"userToken":[]}],"responses":{"200":{"description":"Keys.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","description":"Requires `keys:manage`. Idempotent: revoking an already-revoked key returns 204. Takes effect on the next request.","tags":["API keys"],"security":[{"apiKey":["keys:manage"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Revoked.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/credits":{"get":{"operationId":"getCredits","summary":"Credit balance","description":"Requires `usage:read`. Remaining credits of the account, per balance. Reading never creates or grants credits.","tags":["Usage"],"security":[{"apiKey":["usage:read"]},{"userToken":[]}],"responses":{"200":{"description":"The balances.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditBalances"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/usage":{"get":{"operationId":"getUsage","summary":"Generation usage","description":"Requires `usage:read`. Generations and credits used by the whole account (every API key and surface) in `[from, to)`, bucketed by UTC day, API key or source. A range may span at most 366 days.","tags":["Usage"],"security":[{"apiKey":["usage:read"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Start, inclusive. `YYYY-MM-DD` means 00:00 UTC that day. Default: 00:00 UTC 29 days before the day of `to`.","example":"2026-10-01"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","description":"End, exclusive. `YYYY-MM-DD` includes that whole UTC day. Default: now. At most 366 days after `from`.","example":"2026-10-31"},"required":false,"name":"to","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/UsageGroupBy"},{"default":"day","description":"Bucket by `day` (default), `key` or `source`."}]},"required":false,"name":"groupBy","in":"query"}],"responses":{"200":{"description":"The usage report.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageReport"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/uploads":{"post":{"operationId":"createUpload","summary":"Upload an image","description":"Requires `uploads:write`. Send either `multipart/form-data` with one file part `file`, or JSON `{ \"url\": \"https://…\" }` to have the server fetch the image (https only, public addresses only, at most 3 redirects and 15 seconds). PNG, JPEG or WebP, at most 20 MB and 50 megapixels; the format is detected from the bytes. HEIC is not supported. The upload is `ready` in the response. It is deleted 30 days after creation; generated results are kept until you delete them.","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"Exactly one file part named `file`: PNG, JPEG or WebP, at most 20 MB and 50 megapixels."}}}},"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadFromUrl"}}}},"responses":{"201":{"description":"Created and ready.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE` (the request body is over its limit) or `IMAGE_TOO_LARGE` (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"415":{"description":"`UNSUPPORTED_FORMAT`: not a PNG, JPEG or WebP image (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`URL_FETCH_FAILED`: the URL could not be fetched (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: uploads are busy. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listUploads","summary":"List uploads","description":"Requires `uploads:write`, `generations:write` or `generations:read`. Uploads of the account that are not deleted (every API key and environment), newest first.","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"apiKey":["generations:write"]},{"apiKey":["generations:read"]},{"userToken":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"1–100, default 20."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"`nextCursor` of the previous page."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of uploads.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/uploads/presign":{"post":{"operationId":"presignUpload","summary":"Start a presigned upload","description":"Requires `uploads:write`. Returns a pending upload and a URL to `PUT` the file to (from your server; valid 15 minutes), with the exact headers to send. Then call `POST /uploads/{id}/complete`. A pending upload that is never completed is deleted after 24 hours.","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignUploadRequest"}}}},"responses":{"201":{"description":"Pending upload and its presigned URL.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedUpload"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE` (the request body is over its limit) or `IMAGE_TOO_LARGE` (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"415":{"description":"`UNSUPPORTED_FORMAT`: not a PNG, JPEG or WebP image (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/uploads/{id}/complete":{"post":{"operationId":"completeUpload","summary":"Complete a presigned upload","description":"Requires `uploads:write`. Checks the file you PUT (size, format, caps) and makes the upload `ready`. Idempotent for a ready upload. `409 UPLOAD_NOT_READY` with `object_missing` means the file has not arrived yet: PUT it, then retry. A rejected file makes the upload `failed` and is deleted.","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Upload id. An unknown or malformed id is 404.","example":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Ready.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`UPLOAD_NOT_READY`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE` (the request body is over its limit) or `IMAGE_TOO_LARGE` (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"415":{"description":"`UNSUPPORTED_FORMAT`: not a PNG, JPEG or WebP image (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: uploads are busy. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/uploads/{id}":{"get":{"operationId":"getUpload","summary":"Get an upload","description":"Requires `uploads:write`, `generations:write` or `generations:read`. Metadata only, in any status (deleted uploads included).","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"apiKey":["generations:write"]},{"apiKey":["generations:read"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Upload id. An unknown or malformed id is 404.","example":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The upload.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"deleteUpload","summary":"Delete an upload","description":"Requires `uploads:write`. Deletes the file now; the upload keeps its metadata with status `deleted`. Idempotent.","tags":["Uploads"],"security":[{"apiKey":["uploads:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Upload id. An unknown or malformed id is 404.","example":"6f1c1f0e-8a5e-4b8e-9a51-0f0e1d2c3b4a"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Deleted.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/generations":{"post":{"operationId":"createGeneration","summary":"Create a generation","description":"Requires `generations:write`. Turns one screenshot into one store-ready marketing image of exactly the requested size. Costs 1 credit with a live key, charged now and refunded if the generation fails or is canceled before it starts; test keys (`ss_test_…`) are free and return a sample image. Answers `202` with the queued generation (poll `GET /generations/{id}`), or `200` when it is already final (after `Prefer: wait`, or a replay of a finished one). Send an `Idempotency-Key` so a retried request never creates (or charges) twice.","tags":["Generations"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":["integer","null"],"minimum":0,"maximum":60,"description":"Seconds (0–60) to hold the response until the generation finishes. Overrides `Prefer: wait`."},"required":false,"name":"wait","in":"query"},{"schema":{"type":"string","description":"1–255 printable ASCII characters. Retrying with the same key and body returns the original generation (`Idempotent-Replayed: true`) instead of creating and charging another. Keys are kept as long as the generation exists.","example":"order-1234-shot-1"},"required":false,"in":"header","name":"idempotency-key"},{"schema":{"type":"string","description":"`wait=<seconds>` (at most 60): hold the response until the generation finishes.","example":"wait=30"},"required":false,"in":"header","name":"prefer"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGenerationRequest"}}}},"responses":{"200":{"description":"Final already (waited, or a replay of a finished generation).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Preference-Applied":{"description":"`wait=<seconds>` when the `Prefer: wait` (or `?wait`) request was honoured.","schema":{"type":"string"}},"Vary":{"description":"`Prefer`.","schema":{"type":"string"}},"Location":{"description":"URL of the generation (`GET` it to poll).","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"`true` when this is the stored answer for a repeated `Idempotency-Key`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"202":{"description":"Accepted: queued or running.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Preference-Applied":{"description":"`wait=<seconds>` when the `Prefer: wait` (or `?wait`) request was honoured.","schema":{"type":"string"}},"Vary":{"description":"`Prefer`.","schema":{"type":"string"}},"Location":{"description":"URL of the generation (`GET` it to poll).","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"`true` when this is the stored answer for a repeated `Idempotency-Key`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"`INSUFFICIENT_CREDITS`: nothing was created or charged. See `details.availableCredits` and `details.topUpUrl`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`IDEMPOTENCY_CONFLICT` (the `Idempotency-Key` was used for a different request; `details.generationId`) or `UPLOAD_NOT_READY` (an input upload is not usable; `details.reason`, `details.path`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE` (the JSON body is over 1 MB; refused before authentication, without RateLimit headers) or `IMAGE_TOO_LARGE` (a URL input is too large; see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"415":{"description":"`UNSUPPORTED_FORMAT`: not a PNG, JPEG or WebP image (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`NOT_A_SCREENSHOT` (an input is not screenshot-sized; `details.path`) or `URL_FETCH_FAILED` (a URL input could not be fetched; see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: uploads are busy. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listGenerations","summary":"List generations","description":"Requires `generations:read` or `generations:write`. Generations of the account that are not deleted (every API key and environment), newest first.","tags":["Generations"],"security":[{"apiKey":["generations:read"]},{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"1–100, default 20."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"`nextCursor` of the previous page."},"required":false,"name":"cursor","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/GenerationStatus"},{"description":"Only generations in this status."}]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","maxLength":600,"description":"`<key>:<value>`: only generations whose metadata has exactly this value.","example":"campaign:spring"},"required":false,"name":"metadata","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Only the images of this screenshot set (every attempt)."},"required":false,"name":"screenshotSetId","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKeyEnvironment"},{"description":"SS-013: only `live` or only `test` generations (omitted: both). Keep the same filters while paging."}]},"required":false,"name":"environment","in":"query"}],"responses":{"200":{"description":"A page of generations.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/generations/{id}":{"get":{"operationId":"getGeneration","summary":"Get a generation","description":"Requires `generations:read` or `generations:write`. Includes deleted generations (`deletedAt` set, `result` null). Every call returns freshly signed result URLs (valid 15 minutes). `Prefer: wait=<seconds>` (or `?wait`) holds the response until it is final.","tags":["Generations"],"security":[{"apiKey":["generations:read"]},{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Generation id. An unknown or malformed id is 404.","example":"0b8e7a52-41c6-4f38-9d0e-7f6a1c2b3d4e"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":60,"description":"Seconds (0–60) to hold the response until the generation finishes. Overrides `Prefer: wait`."},"required":false,"name":"wait","in":"query"},{"schema":{"type":"string","description":"`wait=<seconds>` (at most 60): hold the response until the generation finishes.","example":"wait=30"},"required":false,"in":"header","name":"prefer"}],"responses":{"200":{"description":"Final: succeeded, failed or canceled.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Preference-Applied":{"description":"`wait=<seconds>` when the `Prefer: wait` (or `?wait`) request was honoured.","schema":{"type":"string"}},"Vary":{"description":"`Prefer`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"202":{"description":"Not final yet: queued or running.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Preference-Applied":{"description":"`wait=<seconds>` when the `Prefer: wait` (or `?wait`) request was honoured.","schema":{"type":"string"}},"Vary":{"description":"`Prefer`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"deleteGeneration","summary":"Delete a generation","description":"Requires `generations:write`. Deletes the result files of a finished generation now; the generation keeps its metadata with `deletedAt` set and no longer appears in lists. Idempotent. A queued or running generation cannot be deleted (`409 GENERATION_STATE_CONFLICT`): cancel it or wait.","tags":["Generations"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Generation id. An unknown or malformed id is 404.","example":"0b8e7a52-41c6-4f38-9d0e-7f6a1c2b3d4e"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Deleted.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`GENERATION_STATE_CONFLICT`: not possible in the current status (`details.status`, `details.action`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/generations/{id}/cancel":{"post":{"operationId":"cancelGeneration","summary":"Cancel a generation","description":"Requires `generations:write`. Only a `queued` generation that has not started can be canceled; its credit is refunded. Canceling a canceled generation returns it again. Once it is running it cannot be canceled (`409 GENERATION_STATE_CONFLICT`).","tags":["Generations"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Generation id. An unknown or malformed id is 404.","example":"0b8e7a52-41c6-4f38-9d0e-7f6a1c2b3d4e"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Canceled.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`GENERATION_STATE_CONFLICT`: not possible in the current status (`details.status`, `details.action`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/screenshot-sets":{"post":{"operationId":"createScreenshotSet","summary":"Create a screenshot set","description":"Requires `generations:write`. Turns 1–10 screenshots into a full store listing: every screen on every requested (or default) device of one or both stores, plus an optional Google Play feature graphic, with one caption per screen and one palette and logo for all. Each image is one generation (`GET /generations?screenshotSetId=`) and costs 1 credit with a live key: the whole set is created and charged at once, or nothing is (`402` with `details.requiredCredits`). Failed images are refunded; retry them with `POST /screenshot-sets/{id}/retry`. Test keys are free. `?dryRun=true` validates the same way and answers `200` with a quote (images and credits) without creating or charging anything. Send an `Idempotency-Key` so a retried request never creates (or charges) twice.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","enum":["true","false"],"description":"`true`: validate and quote only (`200 ScreenshotSetQuote`); nothing is created or charged."},"required":false,"name":"dryRun","in":"query"},{"schema":{"type":"string","description":"1–255 printable ASCII characters. Retrying with the same key and body returns the original generation (`Idempotent-Replayed: true`) instead of creating and charging another. Keys are kept as long as the generation exists.","example":"order-1234-shot-1"},"required":false,"in":"header","name":"idempotency-key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateScreenshotSetRequest"}}}},"responses":{"200":{"description":"`?dryRun=true`: the quote (nothing created or charged). Or a replay of a finished set.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Location":{"description":"URL of the screenshot set (`GET` it to poll).","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"`true` when this is the stored answer for a repeated `Idempotency-Key`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ScreenshotSetQuote"},{"$ref":"#/components/schemas/ScreenshotSet"}]}}}},"202":{"description":"Accepted: every image is queued.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Location":{"description":"URL of the screenshot set (`GET` it to poll).","schema":{"type":"string"}},"Idempotent-Replayed":{"description":"`true` when this is the stored answer for a repeated `Idempotency-Key`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreenshotSet"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"`INSUFFICIENT_CREDITS`: nothing was created or charged. See `details.availableCredits` and `details.topUpUrl`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`IDEMPOTENCY_CONFLICT` (the `Idempotency-Key` was used for a different request; `details.screenshotSetId`) or `UPLOAD_NOT_READY` (an input upload is not usable; `details.reason`, `details.path`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`NOT_A_SCREENSHOT`: an input is not screenshot-sized (`details.path`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: uploads are busy. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listScreenshotSets","summary":"List screenshot sets","description":"Requires `generations:read` or `generations:write`. Screenshot sets of the account that are not deleted (every API key and environment), newest first.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:read"]},{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"1–100, default 20."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"`nextCursor` of the previous page."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","maxLength":600,"description":"`<key>:<value>`: only sets whose metadata has exactly this value.","example":"release:2.4"},"required":false,"name":"metadata","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKeyEnvironment"},{"description":"SS-013: only `live` or only `test` sets (omitted: both). Keep the same filters while paging."}]},"required":false,"name":"environment","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/ScreenshotSetStatus"},{"description":"SS-013: only sets in this computed status (see `ScreenshotSetStatus`). Keep the same filters while paging."}]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"A page of screenshot sets.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreenshotSetList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/screenshot-sets/{id}":{"get":{"operationId":"getScreenshotSet","summary":"Get a screenshot set","description":"Requires `generations:read` or `generations:write`. The status is computed from the current image of every slot. Includes deleted sets (`deletedAt` set). Poll every few seconds until the status is final (`succeeded`, `partially_failed`, `failed` or `canceled`).","tags":["Screenshot sets"],"security":[{"apiKey":["generations:read"]},{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Screenshot set id. An unknown or malformed id is 404.","example":"5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The screenshot set.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreenshotSet"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"deleteScreenshotSet","summary":"Delete a screenshot set","description":"Requires `generations:write`. Deletes the result files of every image (retries included) and marks the set deleted; it keeps its record with `deletedAt` set and no longer appears in lists. Idempotent. Not possible while images are queued or running (`409 SCREENSHOT_SET_STATE_CONFLICT`): cancel the set or wait.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Screenshot set id. An unknown or malformed id is 404.","example":"5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Deleted.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`SCREENSHOT_SET_STATE_CONFLICT`: not possible in the current status (`details.status`, `details.action`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/screenshot-sets/{id}/retry":{"post":{"operationId":"retryScreenshotSet","summary":"Retry failed images of a screenshot set","description":"Requires `generations:write` and a credential of the set’s environment. Creates a new image (attempt + 1, at most 5 per slot) for every failed or canceled image, or for the given `generationIds`, at 1 credit each with a live key, all at once or none (`402`). Images that are superseded, still running or succeeded are skipped (`retry.skipped`); if nothing is left to retry the answer is `409 SCREENSHOT_SET_STATE_CONFLICT` and nothing is charged, so repeating a retry never charges twice.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Screenshot set id. An unknown or malformed id is 404.","example":"5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryScreenshotSetRequest"}}}},"responses":{"202":{"description":"Accepted: the new images are queued.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetriedScreenshotSet"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"`INSUFFICIENT_CREDITS`: nothing was created or charged. See `details.availableCredits` and `details.topUpUrl`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`SCREENSHOT_SET_STATE_CONFLICT` (nothing to retry; `details.status`) or `UPLOAD_NOT_READY` (an input upload expired or was deleted; `details.path`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/screenshot-sets/{id}/cancel":{"post":{"operationId":"cancelScreenshotSet","summary":"Cancel a screenshot set","description":"Requires `generations:write`. Cancels every image that has not started yet (each is refunded); running images finish normally. Canceling a canceled set returns it again; a set that already finished otherwise is `409 SCREENSHOT_SET_STATE_CONFLICT`.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Screenshot set id. An unknown or malformed id is 404.","example":"5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The set after canceling its queued images.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreenshotSet"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`SCREENSHOT_SET_STATE_CONFLICT`: not possible in the current status (`details.status`, `details.action`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/screenshot-sets/{id}/archive":{"get":{"operationId":"getScreenshotSetArchive","summary":"Download a screenshot set as a ZIP","description":"Requires `generations:read` or `generations:write`. Streams a ZIP of every succeeded image (the current attempt of each slot) and a `manifest.json` listing the files and the missing images. `layout=store` (default): `<store>/<device>/<NN>.<ext>`. `layout=fastlane`: `fastlane/screenshots/en-US/<NN>_<device>.png` for `deliver` and `fastlane/metadata/android/en-US/images/{phoneScreenshots,sevenInchScreenshots,tenInchScreenshots}/<NN>.png` + `featureGraphic.jpg` for `supply` (note: `supply` replaces the images already in your listing). `409 SCREENSHOT_SET_STATE_CONFLICT` when no image has succeeded yet; `503` with `Retry-After` when the server is busy building other archives.","tags":["Screenshot sets"],"security":[{"apiKey":["generations:read"]},{"apiKey":["generations:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Screenshot set id. An unknown or malformed id is 404.","example":"5c1e2d3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["store","fastlane"],"default":"store","description":"`store`: `<store>/<device>/<NN>.<ext>`. `fastlane`: `fastlane/screenshots/en-US/` (deliver) and `fastlane/metadata/android/en-US/images/` (supply)."},"required":false,"name":"layout","in":"query"}],"responses":{"200":{"description":"The ZIP (streamed).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Content-Disposition":{"description":"`attachment; filename=\"<app>-screenshots.zip\"` (or `-fastlane.zip`).","schema":{"type":"string"}}},"content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`SCREENSHOT_SET_STATE_CONFLICT`: not possible in the current status (`details.status`, `details.action`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: too many archives are being built. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/palettes":{"post":{"operationId":"createPalette","summary":"Extract a brand palette","description":"Requires `generations:write`. Free: no credits, no AI model; the same result for the same image. `extract` (default) returns up to `maxColors` distinct colours of one image, exactly as the Storeshot app does; `dominant` returns the dominant colour of each image (an image that cannot be analysed is skipped). When no colour can be found, `source` is `default` and `colors` is the Storeshot default palette. URL inputs are fetched for this request only (same rules as `POST /uploads`) and never stored.","tags":["Palettes"],"security":[{"apiKey":["generations:write"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaletteRequest"}}}},"responses":{"200":{"description":"The palette.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Palette"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`UPLOAD_NOT_READY`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE` (the request body is over its limit) or `IMAGE_TOO_LARGE` (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"415":{"description":"`UNSUPPORTED_FORMAT`: not a PNG, JPEG or WebP image (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`URL_FETCH_FAILED`: the URL could not be fetched (see `details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: uploads are busy. Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/stores":{"get":{"operationId":"listStores","summary":"List stores","description":"The stores Storeshot targets. Any valid API key or access token; no scope required.","tags":["Catalog"],"security":[{"apiKey":[]},{"userToken":[]}],"responses":{"200":{"description":"The stores.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/stores/{store}/requirements":{"get":{"operationId":"getStoreRequirements","summary":"Get store screenshot requirements","description":"Formats, counts, sizes or dimension rules per device class, which classes are needed, and the Google Play feature graphic, with the official sources they come from. Each class lists the `GET /devices` sizes it covers and whether they meet the store rules. Any valid API key or access token; no scope required.","tags":["Catalog"],"security":[{"apiKey":[]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"`app_store` or `google_play`. Anything else is 404.","example":"google_play"},"required":true,"name":"store","in":"path"}],"responses":{"200":{"description":"The requirements.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreRequirements"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks":{"post":{"operationId":"createWebhook","summary":"Create a webhook","description":"Requires `webhooks:write` (not a default scope). Registers an `https://` endpoint (port 443, public addresses only) for the given events of one environment; at most 10 per environment. The signing secret is in this response only.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:write"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"201":{"description":"Created. Store `secret.value` now; it is never shown again.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`WEBHOOK_URL_REJECTED`: the host is not public or does not resolve (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: secrets cannot be issued right now.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listWebhooks","summary":"List webhooks","description":"Requires `webhooks:read` or `webhooks:write`. An API key sees the webhooks of its own environment; an access token sees both (or one with `?environment=`). Secrets are never returned.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:read"]},{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKeyEnvironment"},{"description":"Access tokens: only webhooks of this environment (default: both). API keys always see their own environment only."}]},"required":false,"name":"environment","in":"query"}],"responses":{"200":{"description":"Webhooks, newest first.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}":{"get":{"operationId":"getWebhook","summary":"Get a webhook","description":"Requires `webhooks:read` or `webhooks:write`. The secret is never returned.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:read"]},{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The webhook.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"operationId":"updateWebhook","summary":"Update a webhook","description":"Requires `webhooks:write`. Send any of `url`, `events`, `description`, `enabled`, `creditsLowThreshold`. A new `url` is checked like on create. While a webhook is disabled it gets no new events, and a pending delivery reaching its next attempt is canceled.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"The updated webhook.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`WEBHOOK_URL_REJECTED`: the host is not public or does not resolve (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","description":"Requires `webhooks:write`. Deletes the webhook with its delivery log; pending deliveries are dropped.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Deleted.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Rotate the signing secret","description":"Requires `webhooks:write`. Issues a new secret (in this response only). For `gracePeriodSeconds` (default 24 hours) deliveries carry a signature with each secret, so receivers can switch without dropping events.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateWebhookSecretRequest"}}}},"responses":{"200":{"description":"Rotated. Store `secret.value` now; it is never shown again.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: secrets cannot be issued right now.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/test":{"post":{"operationId":"testWebhook","summary":"Send a test event","description":"Requires `webhooks:write`. Queues a signed `webhook.test` event to this webhook (whatever its `events`); follow it in `GET /webhooks/{id}/deliveries`. At most 10 per webhook per hour.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Queued.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResult"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`WEBHOOK_STATE_CONFLICT`: the webhook is disabled (`details.action` `test`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List deliveries","description":"Requires `webhooks:read` or `webhooks:write`. Deliveries of this webhook, newest first, with every attempt (status code, latency, error class and the start of the response body). Kept 30 days after they finish.","tags":["Webhooks"],"security":[{"apiKey":["webhooks:read"]},{"apiKey":["webhooks:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Webhook id. An unknown or malformed id is 404.","example":"6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"1–100, default 20."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"`nextCursor` of the previous page."},"required":false,"name":"cursor","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookDeliveryStatus"},{"description":"Only deliveries in this status."}]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"A page of deliveries.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/packs":{"get":{"operationId":"listBillingPacks","summary":"List credit packs","description":"Requires `billing:read` or `billing:write` (not default scopes). The one-time credit packs on sale, with list prices in cents; taxes are applied at checkout by the payment provider (merchant of record). Empty while web payments are switched off. Any key environment may list.","tags":["Billing"],"security":[{"apiKey":["billing:read"]},{"apiKey":["billing:write"]},{"userToken":[]}],"responses":{"200":{"description":"The packs on sale.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPackList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/checkout":{"post":{"operationId":"createBillingCheckout","summary":"Start a checkout","description":"Requires `billing:write` (not a default scope) and a live credential. Creates a hosted checkout for one pack and returns its `url`: send the buyer there. Credits are added to `purchased` once the payment succeeds (poll `GET /billing/checkouts/{id}`); a refund takes back what is still unused. Checkouts expire after 24 hours. At most 10 per account per hour. Send an `Idempotency-Key` so a retried request never opens two checkouts.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"1–255 printable ASCII characters. Retrying with the same key and body returns the original checkout (`Idempotent-Replayed: true`) instead of creating another.","example":"buy-15-2026-10-22"},"required":false,"in":"header","name":"idempotency-key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBillingCheckoutRequest"}}}},"responses":{"201":{"description":"Created (or, with `Idempotent-Replayed: true`, the checkout created earlier with this key and body).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Idempotent-Replayed":{"description":"`true` when this is the stored answer for a repeated `Idempotency-Key`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingCheckout"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope`, `live_key_required` or `billing_account_flagged` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`IDEMPOTENCY_CONFLICT`: the key was used for a different body, or that checkout is still being created (`details.checkoutId`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: request rate (see `details.scope`), or 10 checkouts in the last hour (`details.policy` `billing_checkouts`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing was charged.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/checkouts/{id}":{"get":{"operationId":"getBillingCheckout","summary":"Get a checkout","description":"Requires `billing:read` or `billing:write` and a live credential. The checkout status, derived from the payment provider: `open`, `processing`, `paid`, `partially_refunded`, `refunded`, `expired` or `failed`, with the credits granted and reversed so far. Only the account that created it can read it. `{id}` is our checkout id or the payment provider's checkout id (the `{CHECKOUT_ID}` placeholder of `successUrl`), so a success page can look the checkout up straight from its URL. Plan checkouts also return `planName` and `subscriptionStatus`.","tags":["Billing"],"security":[{"apiKey":["billing:read"]},{"apiKey":["billing:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"Our checkout id (`id`) or the payment provider's checkout id (what `{CHECKOUT_ID}` in your `successUrl` becomes); our id wins if both match. An unknown, foreign or malformed id is 404.","example":"3f0c9a2e-5d1b-4c7e-9a8f-2b6d4e1c0a9f"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The checkout.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingCheckout"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`NOT_FOUND`: no such resource for this caller.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/plans":{"get":{"operationId":"listBillingPlans","summary":"List plans","description":"Requires `billing:read` or `billing:write` (not default scopes). The monthly plans on sale, with list prices in cents (taxes are applied at checkout) and the limits each plan raises. Each paid month resets the `subscription` balance to the plan credits; subscription credits are used before starter and purchased credits and expire at renewal. Empty while plans are switched off. Any key environment may list.","tags":["Billing"],"security":[{"apiKey":["billing:read"]},{"apiKey":["billing:write"]},{"userToken":[]}],"responses":{"200":{"description":"The plans on sale.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPlanList"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/subscription":{"get":{"operationId":"getBillingSubscription","summary":"Get the plan of this account","description":"Requires `billing:read` or `billing:write`. The tier whose limits apply now (`free` without a plan), those limits, and the latest subscription with its `subscription` balance. Any key environment may read.","tags":["Billing"],"security":[{"apiKey":["billing:read"]},{"apiKey":["billing:write"]},{"userToken":[]}],"responses":{"200":{"description":"The plan state.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSubscriptionState"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: see `details.reason`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/subscription/checkout":{"post":{"operationId":"createBillingSubscriptionCheckout","summary":"Start a plan checkout","description":"Requires `billing:write` and a live credential. Creates a hosted checkout for a monthly plan and returns its `url`. Once the first payment succeeds the plan applies and the `subscription` balance is set to the plan credits (poll `GET /billing/subscription`). One plan per account: an account with a plan changes it instead. Asking again for the same plan within 15 minutes returns the open checkout; another plan replaces it. At most 10 checkouts per account per hour (packs included). Send an `Idempotency-Key` so a retried request never opens two checkouts.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"parameters":[{"schema":{"type":"string","description":"1–255 printable ASCII characters. Retrying with the same key and body returns the original checkout (`Idempotent-Replayed: true`) instead of creating another.","example":"buy-15-2026-10-22"},"required":false,"in":"header","name":"idempotency-key"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionCheckoutRequest"}}}},"responses":{"201":{"description":"Created (or, with `Idempotent-Replayed: true`, the open checkout created earlier for this plan or this key).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Idempotent-Replayed":{"description":"`true` when this is an earlier checkout returned again.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingCheckout"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope`, `live_key_required` or `billing_account_flagged` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`BILLING_STATE_CONFLICT` (`subscription_exists`, `checkout_pending`) or `IDEMPOTENCY_CONFLICT`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: request rate (see `details.scope`), or 10 checkouts in the last hour (`details.policy` `billing_checkouts`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing changed.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/subscription/change":{"post":{"operationId":"changeBillingSubscription","summary":"Change the plan","description":"Requires `billing:write` and a live credential. A higher plan applies at once: the prorated difference is charged now and, once paid, the `subscription` balance grows by the extra credits for the rest of the period. A lower plan starts at the next renewal (`pendingPlan`). Asking for the current plan while a downgrade is pending cancels the downgrade.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeSubscriptionRequest"}}}},"responses":{"200":{"description":"The plan state after the change (credits move only when the provider reports payment).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSubscriptionState"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope` or `live_key_required` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`BILLING_STATE_CONFLICT`: `no_subscription`, `same_plan`, `canceling` or `past_due` (`details.reason`). Nothing was charged.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing changed.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/subscription/cancel":{"post":{"operationId":"cancelBillingSubscription","summary":"Cancel the plan at the period end","description":"Requires `billing:write` and a live credential. The plan, its limits and its credits stay until the period ends; nothing renews after that. Works while plans are switched off, so a subscriber can always stop paying.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"responses":{"200":{"description":"The plan state after the change (credits move only when the provider reports payment).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSubscriptionState"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope` or `live_key_required` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`BILLING_STATE_CONFLICT`: `no_subscription` (`details.reason`). Nothing was charged.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing changed.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/subscription/uncancel":{"post":{"operationId":"uncancelBillingSubscription","summary":"Keep the plan (undo a cancel)","description":"Requires `billing:write` and a live credential. Undoes a cancel before the period ends; the plan renews as before.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"responses":{"200":{"description":"The plan state after the change (credits move only when the provider reports payment).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSubscriptionState"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope` or `live_key_required` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`BILLING_STATE_CONFLICT`: `no_subscription` (`details.reason`). Nothing was charged.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing changed.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/billing/portal":{"post":{"operationId":"createBillingPortalSession","summary":"Open the billing portal","description":"Requires `billing:write` and a live credential. Returns a link to the payment provider portal (invoices, payment method, cancel). `mode: session` is signed in as this account; it is offered only when the payment account belongs to this account email. Otherwise `mode: email_login` asks for the billing email and sends a one-time code.","tags":["Billing"],"security":[{"apiKey":["billing:write"]},{"userToken":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePortalSessionRequest"}}}},"responses":{"200":{"description":"The portal link.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPortalSession"}}}},"400":{"description":"`INVALID_INPUT`. A credential sent in the query string is refused before authentication, without RateLimit headers.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`UNAUTHORIZED`: missing, malformed, unknown, revoked or expired credential.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`FORBIDDEN`: `insufficient_scope` or `live_key_required` (`details.reason`).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`BILLING_STATE_CONFLICT`: `no_customer` (`details.reason`). Nothing was charged.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`RATE_LIMITED`: the request rate of the API key (`details.scope` `key`) or of the account (`user`) is exhausted for this window, or too many failed authentication attempts came from this IP (no `details.scope` and no RateLimit headers). Retry after `Retry-After` seconds.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying (equals `details.retryAfterSeconds`).","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`BILLING_UNAVAILABLE`: `disabled`, `not_configured` or `provider_error` (`details.reason`). Nothing changed.","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"default":{"description":"Error (see ErrorEnvelope and /v1/docs/errors).","headers":{"X-Request-Id":{"description":"Echoes a valid incoming X-Request-Id, otherwise a generated UUID.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota of the most restrictive request policy (draft-ietf-httpapi-ratelimit-headers): `r` requests left, `t` seconds until the window resets. Absent when the request was not authenticated.","schema":{"type":"string"},"example":"\"key\";r=17;t=42"},"RateLimit-Policy":{"description":"Every request policy applied to this credential: `key` (per API key) and `user` (whole account); `q` requests per `w` seconds. Access tokens have only `user`.","schema":{"type":"string"},"example":"\"key\";q=60;w=60, \"user\";q=120;w=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"webhooks":{"generation.succeeded":{"post":{"operationId":"webhook_generation_succeeded","summary":"A generation succeeded","description":"Sent as a signed `POST` to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify `Storeshot-Signature` over the raw body before parsing it.","tags":["Webhooks"],"security":[],"parameters":[{"schema":{"type":"string","description":"`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook secret>`; during a rotation grace a second `v1` (the previous secret) follows. Reject `t` older than 5 minutes.","example":"t=1761127200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},"required":true,"name":"storeshot-signature","in":"header"},{"schema":{"type":"string","description":"Identical on every attempt of this event: deduplicate on it."},"required":true,"name":"storeshot-event-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"storeshot-event-type","in":"header"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"storeshot-delivery-id","in":"header"},{"schema":{"type":"string","description":"1–8."},"required":true,"name":"storeshot-delivery-attempt","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationSucceededEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery (the body is ignored)."}}}},"generation.failed":{"post":{"operationId":"webhook_generation_failed","summary":"A generation failed or was canceled","description":"Sent as a signed `POST` to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify `Storeshot-Signature` over the raw body before parsing it.","tags":["Webhooks"],"security":[],"parameters":[{"schema":{"type":"string","description":"`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook secret>`; during a rotation grace a second `v1` (the previous secret) follows. Reject `t` older than 5 minutes.","example":"t=1761127200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},"required":true,"name":"storeshot-signature","in":"header"},{"schema":{"type":"string","description":"Identical on every attempt of this event: deduplicate on it."},"required":true,"name":"storeshot-event-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"storeshot-event-type","in":"header"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"storeshot-delivery-id","in":"header"},{"schema":{"type":"string","description":"1–8."},"required":true,"name":"storeshot-delivery-attempt","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationFailedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery (the body is ignored)."}}}},"screenshot_set.completed":{"post":{"operationId":"webhook_screenshot_set_completed","summary":"A screenshot set reached a final status","description":"Sent as a signed `POST` to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify `Storeshot-Signature` over the raw body before parsing it.","tags":["Webhooks"],"security":[],"parameters":[{"schema":{"type":"string","description":"`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook secret>`; during a rotation grace a second `v1` (the previous secret) follows. Reject `t` older than 5 minutes.","example":"t=1761127200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},"required":true,"name":"storeshot-signature","in":"header"},{"schema":{"type":"string","description":"Identical on every attempt of this event: deduplicate on it."},"required":true,"name":"storeshot-event-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"storeshot-event-type","in":"header"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"storeshot-delivery-id","in":"header"},{"schema":{"type":"string","description":"1–8."},"required":true,"name":"storeshot-delivery-attempt","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreenshotSetCompletedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery (the body is ignored)."}}}},"credits.low":{"post":{"operationId":"webhook_credits_low","summary":"The live credit balance fell below the threshold","description":"Sent as a signed `POST` to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify `Storeshot-Signature` over the raw body before parsing it.","tags":["Webhooks"],"security":[],"parameters":[{"schema":{"type":"string","description":"`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook secret>`; during a rotation grace a second `v1` (the previous secret) follows. Reject `t` older than 5 minutes.","example":"t=1761127200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},"required":true,"name":"storeshot-signature","in":"header"},{"schema":{"type":"string","description":"Identical on every attempt of this event: deduplicate on it."},"required":true,"name":"storeshot-event-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"storeshot-event-type","in":"header"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"storeshot-delivery-id","in":"header"},{"schema":{"type":"string","description":"1–8."},"required":true,"name":"storeshot-delivery-attempt","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditsLowEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery (the body is ignored)."}}}},"webhook.test":{"post":{"operationId":"webhook_webhook_test","summary":"Test event (POST /webhooks/{id}/test)","description":"Sent as a signed `POST` to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify `Storeshot-Signature` over the raw body before parsing it.","tags":["Webhooks"],"security":[],"parameters":[{"schema":{"type":"string","description":"`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook secret>`; during a rotation grace a second `v1` (the previous secret) follows. Reject `t` older than 5 minutes.","example":"t=1761127200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},"required":true,"name":"storeshot-signature","in":"header"},{"schema":{"type":"string","description":"Identical on every attempt of this event: deduplicate on it."},"required":true,"name":"storeshot-event-id","in":"header"},{"schema":{"type":"string"},"required":true,"name":"storeshot-event-type","in":"header"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"storeshot-delivery-id","in":"header"},{"schema":{"type":"string","description":"1–8."},"required":true,"name":"storeshot-delivery-attempt","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the delivery (the body is ignored)."}}}}}}