{"openapi":"3.1.0","info":{"title":"CoverScale API","description":"Generate release assets from one piece of artwork.\n\n## Authenticating\n\nEvery endpoint takes an API key as a bearer token:\n\n```\nAuthorization: Bearer cs_live_<key_id>_<secret>\n```\n\nAn API key is the **only** credential this API accepts. There is no browser\nsession, no ID token and no App Check header involved — a request is\nauthenticated entirely by the key in that one header, which is what makes it\ncallable from your backend.\n\nKeys are issued from your CoverScale account, not through this API — a key\ncannot mint another key. `GET /v1/me` confirms a key works and reports the\nscopes, per-minute budget and `tier` it carries.\n\n## API tier\n\nAccess is granted per account, and by default an account has none — a key\ncannot be minted until it is. Two tiers:\n\n- **`basic`** — still asset types, and the `ken_burns` and `split_layer`\n  motions. Every request stays on our own hardware, so this is the tier for\n  an integration you are building or testing: no call ever reaches a\n  video-generation provider.\n- **`full`** — every pipeline the API supports.\n\nA request outside the account's tier is refused with `403 tier_forbidden`\nbefore anything is charged. `GET /v1/generations/{type}/estimate` lists every\nprice for that type and, for video, marks which motions this tier can submit.\n\n## Generating\n\nThere is one endpoint per asset type, each accepting only the fields that\napply to it:\n\n| Endpoint | Delivers |\n|---|---|\n| `POST /v1/generations/avatars` | Profile artwork at every DSP's avatar size |\n| `POST /v1/generations/banners` | Header artwork for every DSP banner shape |\n| `POST /v1/generations/vinyl` | A physical-media mockup of the cover |\n| `POST /v1/generations/spotify-canvas` | A 9:16 looping Spotify Canvas clip, up to 8 seconds |\n| `POST /v1/generations/youtube-visualizer` | A 16:9 visualizer, about 8 to 12 seconds, optionally muxed to a track |\n| `POST /v1/generations/apple-motion` | 15-second Apple Music motion artwork, both shapes |\n| `POST /v1/generations/reels` | A 15-second 9:16 clip for Reels and Shorts |\n\nEach returns immediately with a job. Poll `GET /v1/jobs/{job_id}` until\n`status` is `succeeded`, `failed` or `cancelled`, or supply a `webhook_url`\nand be told.\n\nEvery endpoint has a `GET /estimate` twin — `GET\n/v1/generations/spotify-canvas/estimate`, and so on — which takes no body.\nVideo types return one price per motion. Vinyl returns the front-only price\nand the price with a back cover. Avatars and banners have a single price.\nThe artwork itself never changes the charge, and neither do `prompt`,\n`audio_url`, typography, vinyl color or label art. `high`,\n`calm` and `custom_prompt` are the same price.\n\nSend this header on every submit, and send the same value again if you retry:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job rather than charging\nagain. Reusing a key with a *different* body, or against a *different*\nendpoint, is a `409` — that almost always means a bug, and returning the\nearlier job would hand you an asset you did not ask for.\n\n`POST /v1/jobs/{job_id}/cancel` asks a job to stop. A job that has not\nstarted yet is cancelled and refunded immediately. A job that is already\ngenerating stops at its next stage boundary and is **not refunded** — the\nprovider work it triggered has already been paid for — so cancel promptly or\nnot at all. Either way the job's final `status` is `cancelled`.\n\n## Delivered files\n\nA finished job carries `outputs`, one entry per platform spec — a single file\nfor most asset types, several where one generation legitimately produces\nseveral. Each `url` is signed and short-lived; poll again for a fresh one\nrather than storing it.\n\n**Video outputs are silent by default.** Each endpoint chooses the length:\nSpotify Canvas stays within 8 seconds, the YouTube visualizer is about 8 to 12\nseconds, and Apple Motion and Reels / Shorts are 15 seconds.\n`/v1/generations/youtube-visualizer` is the exception on audio: supply its\n`audio_url` and the delivered video is looped and muxed to that track's own\nlength instead. No other endpoint has an `audio_url` field, because nothing\nmuxes a track in for them.\n\n`/v1/generations/apple-motion` delivers both a `1x1` and a `3x4` file from one\ngeneration and one charge, for every motion including `high`, `calm` and\n`custom_prompt`.\n\n## Vinyl mockups\n\n`/v1/generations/vinyl` composites your cover art into a physical-media mockup\nrather than resizing it: the artwork is placed onto a record sleeve, optionally\nwith the record pulled partway out and its centre label showing. That endpoint\ndocuments each of its own fields; the rules worth knowing before you read them\nare that `sleeve12` has no back cover at all, that a back cover is a second\nrender from its own source rather than a crop (so it bills a second credit),\nand that custom label art is only accepted for a side that actually shows a\nlabel. A contradictory combination is a `400 invalid_option` naming which\nfield to change, before anything is charged.\n\n## Webhooks\n\nWhen a job reaches a terminal state we POST the job to your `webhook_url` with\ntwo headers:\n\n- `CoverScale-Event` — `generation.succeeded`, `generation.failed` or\n  `generation.cancelled`\n- `CoverScale-Signature` — `t=<unix>,v1=<hex>`\n\nVerify it by recomputing HMAC-SHA256 over `\"{t}.{raw_body}\"` with the\n`webhook_secret` you were given when the key was created, and comparing\nagainst `v1` in constant time. Reject anything whose `t` is older than your\nchosen tolerance.\n\nWebhook delivery is a convenience with bounded retries. **Polling is the\nguaranteed path** — if an endpoint of yours is unreachable, the asset was\nstill produced and `GET /v1/jobs/{job_id}` will show it.\n\n## Errors\n\nEvery failure carries `{\"error\": \"<code>\", \"message\": \"<prose>\"}`. Branch on\n`error`; `message` is for humans and may change. `429` means the key's\nper-minute budget is spent, `402` means the account is out of credits, and\n`403` with `api_access_disabled` or `tier_forbidden` means the account or its\ntier does not cover this request — see [API tier](#api-tier).\n\nTwo kinds of rejection are worth telling apart:\n\n- **`422 invalid_request`** — the body does not match the endpoint's schema: a\n  field is missing, malformed, or does not exist on that endpoint. Since each\n  endpoint accepts only the fields that apply to it, sending `vinyl_color` to\n  `/avatars` or `motion` to `/banners` lands here rather than being quietly\n  ignored. A `fields` array names each offending path.\n- **`400 invalid_option`** — every field is individually valid but the\n  *combination* is not, which today means the vinyl rules above.\n\nBoth are raised before anything is charged.","version":"1.0.0"},"servers":[{"url":"https://api.coverscale.app","description":"Production"},{"url":"https://api-staging.coverscale.app","description":"Staging"}],"paths":{"/v1/me":{"get":{"tags":["Account"],"summary":"Whoami","description":"Confirm a key works, and report the account, scopes and budget it carries.\n\nThe first call a partner makes, and the one to reach for when an\nintegration is failing and it is not yet clear whether the credential or\nthe request is at fault. Scoped to ``usage:read`` rather than left\nunscoped so that a deliberately narrow key cannot be used to enumerate\nwhat it is attached to.","operationId":"whoami_v1_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerIdentityResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/avatars":{"post":{"tags":["Generations"],"summary":"Generate avatar artwork","description":"Reframe the artwork to every DSP's profile-picture size.\n\nOne detect call frames all of them, so the fan-out costs no more than a\nsingle delivery.","operationId":"submit_avatars_v1_generations_avatars_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvatarsGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/avatars/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate avatar artwork","description":"What `POST /v1/generations/avatars` costs. One price; the body does not change it.","operationId":"estimate_avatars_v1_generations_avatars_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StillEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/banners":{"post":{"tags":["Generations"],"summary":"Generate banner artwork","description":"Reframe the artwork to every DSP's header shape.\n\nA source too narrow to cover a banner's canvas is extended generatively\nrather than letterboxed, so a square cover still fills a wide header.","operationId":"submit_banners_v1_generations_banners_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BannersGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/banners/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate banner artwork","description":"What `POST /v1/generations/banners` costs. One price; the body does not change it.","operationId":"estimate_banners_v1_generations_banners_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StillEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/vinyl":{"post":{"tags":["Generations"],"summary":"Generate a vinyl mockup","description":"Composite the cover art into a physical-media mockup.\n\nNot a resize: the artwork is placed onto a record sleeve, optionally with\nthe record pulled out and its centre label showing. Supplying\n`back_image_url` delivers a second file and bills a second credit.","operationId":"submit_vinyl_v1_generations_vinyl_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VinylGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/vinyl/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate vinyl mockup","description":"What `POST /v1/generations/vinyl` costs, both ways.\n\nA back cover is a second render, so it is the second row. Color, label art\nand `appearance` are the same charge; `sleeve12` simply cannot use the\nback-cover row.","operationId":"estimate_vinyl_v1_generations_vinyl_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VinylEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/spotify-canvas":{"post":{"tags":["Generations"],"summary":"Generate a Spotify Canvas","description":"Animate the artwork into a 9:16 looping Spotify Canvas clip, up to 8 seconds.","operationId":"submit_spotify_canvas_v1_generations_spotify_canvas_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpotifyCanvasGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/spotify-canvas/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate Spotify Canvas","description":"What each motion costs on `POST /v1/generations/spotify-canvas`.","operationId":"estimate_spotify_canvas_v1_generations_spotify_canvas_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/youtube-visualizer":{"post":{"tags":["Generations"],"summary":"Generate a YouTube visualizer","description":"Animate the artwork into a 16:9 visualizer clip, about 8 to 12 seconds.\n\nThe only asset type that takes a track: supply `audio_url` and the clip is\nlooped and muxed to that track's length instead of being silent.","operationId":"submit_youtube_visualizer_v1_generations_youtube_visualizer_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/YoutubeVisualizerGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/youtube-visualizer/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate YouTube visualizer","description":"What each motion costs on `POST /v1/generations/youtube-visualizer`.\n\nSupplying `audio_url` on submit does not change any of these prices.","operationId":"estimate_youtube_visualizer_v1_generations_youtube_visualizer_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/apple-motion":{"post":{"tags":["Generations"],"summary":"Generate Apple Music motion artwork","description":"Animate the artwork into 15-second Apple Music motion artwork.\n\nDelivers both the `1x1` and `3x4` shapes from one generation and one\ncharge — the narrower one is derived from the same clip rather than\ngenerated again.","operationId":"submit_apple_motion_v1_generations_apple_motion_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppleMotionGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/apple-motion/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate Apple Music motion artwork","description":"What each motion costs on `POST /v1/generations/apple-motion`.\n\nLocal motions (`ken_burns`, `split_layer`) are priced per delivered file,\nso they cost twice the other video types. Every other motion is one charge\nfor both shapes.","operationId":"estimate_apple_motion_v1_generations_apple_motion_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/generations/reels":{"post":{"tags":["Generations"],"summary":"Generate a Reels / Shorts clip","description":"Animate the artwork into a 15-second 9:16 clip for Reels and Shorts.","operationId":"submit_reels_v1_generations_reels_post","security":[{"ApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created.","examples":["3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23"],"title":"Idempotency-Key"},"description":"Optional. Any string you can reproduce, sent again if you retry a submit that timed out:\n\n```\nIdempotency-Key: 3f1c0b2e-7a4d-4c1e-9b2a-6d5e8f0a1c23\n```\n\nA retry carrying the same key returns the original job instead of charging again. Reusing a key with a different body, or against a different endpoint, is a `409`. Omitting it is fine — `GET /v1/jobs` will show anything a lost submit created."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReelsGenerationRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Bad Request"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Payment Required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Implemented"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Service Unavailable"}}}},"/v1/generations/reels/estimate":{"get":{"tags":["Generations"],"summary":"Price estimate Reels / Shorts clip","description":"What each motion costs on `POST /v1/generations/reels`.","operationId":"estimate_reels_v1_generations_reels_estimate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEstimateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"ApiKey":[]}]}},"/v1/jobs":{"get":{"tags":["Jobs"],"summary":"List jobs","description":"List this account's generations, newest first.\n\nWhat a caller reconciles against their own records — and the way back to\na job whose submit response never arrived, since the credit is spent\nwhether or not the id was ever stored.\n\nA summary per job, with no files: signing every output on a full page\nwould be hundreds of IAM round-trips for the callers who only wanted to\nmatch ids. Fetch the job itself for its `outputs`.","operationId":"list_account_jobs_v1_jobs_get","security":[{"ApiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Jobs per page.","default":20,"title":"Limit"},"description":"Jobs per page."},{"name":"starting_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[0-9a-f]{32}$"},{"type":"null"}],"description":"The last `job_id` of the previous page. Omit for the newest jobs.","title":"Starting After"},"description":"The last `job_id` of the previous page. Omit for the newest jobs."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponse"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"}}}},"/v1/jobs/{job_id}":{"get":{"tags":["Jobs"],"summary":"Read Job","description":"Poll a generation. Another account's job reads as missing.","operationId":"read_job_v1_jobs__job_id__get","security":[{"ApiKey":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"}}}},"/v1/jobs/{job_id}/cancel":{"post":{"tags":["Jobs"],"summary":"Cancel Job","description":"Ask for a generation to stop.\n\nA flag, not a kill: a job that has not started is settled immediately, and\na running one stops at its next stage boundary. A job that already\nfinished is returned unchanged rather than reported as an error — there is\nnothing to cancel, and saying otherwise would mislead.","operationId":"cancel_job_v1_jobs__job_id__cancel_post","security":[{"ApiKey":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"}}}},"/v1/usage":{"get":{"tags":["Account"],"summary":"Read Usage","description":"Summarise what this account spent, with a per-asset-type breakdown.\n\n``key_id`` is a filter, not an authorisation boundary: it can only narrow\nthe account's own rows, and a key may ask about its siblings because they\nbill to the same balance the caller is already entitled to see.","operationId":"read_usage_v1_usage_get","security":[{"ApiKey":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Window to summarise, in days.","default":30,"title":"Days"},"description":"Window to summarise, in days."},{"name":"key_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"Limit to one key's usage. Omit for the whole account.","title":"Key Id"},"description":"Limit to one key's usage. Omit for the whole account."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerUsageResponse"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Too Many Requests"}}}}},"components":{"schemas":{"AppleMotionGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"motion":{"type":"string","enum":["ken_burns","split_layer","calm","high","custom_prompt","glitch","camera_shift","dolly_zoom","camera_blur","matrix"],"title":"Motion","description":"How to animate the artwork. `GET /estimate` lists the price of each.","examples":["high"],"x-enumDescriptions":{"calm":"Calm drift and subtle movement.","camera_blur":"Dreamy focal-point shift.","camera_shift":"Drifts between two camera angles.","custom_prompt":"Motion follows the `prompt` you send. `prompt` is required.","dolly_zoom":"Classic vertigo depth warp.","glitch":"Randomized placement of artwork elements.","high":"Bold, lively motion.","ken_burns":"Smooth zoom and pan.","matrix":"Bullet-time orbit around the subject.","split_layer":"Artwork over a softly moving backdrop."}},"prompt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Prompt","description":"Motion direction. Required when `motion` is `custom_prompt`, and rejected otherwise.","examples":["slow drifting smoke"]}},"additionalProperties":false,"type":"object","required":["image_url","motion"],"title":"AppleMotionGenerationRequest","description":"15-second Apple Music motion artwork, both shapes from one generation.\n\nEvery motion, including `high`, `calm` and `custom_prompt`, delivers a `1x1` and a `3x4` file from a\nsingle generation and a single charge — the narrower shape is derived from\nthe same clip rather than generated again."},"AvatarsGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"typography":{"type":"string","enum":["auto","remove","preserve"],"title":"Typography","description":"`auto` removes typography only where a target spec forbids it; `remove` strips it from the whole source; `preserve` never removes.","default":"auto"}},"additionalProperties":false,"type":"object","required":["image_url"],"title":"AvatarsGenerationRequest","description":"Profile artwork, delivered at every DSP's avatar size from one detect call."},"BannersGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"typography":{"type":"string","enum":["auto","remove","preserve"],"title":"Typography","description":"`auto` removes typography only where a target spec forbids it; `remove` strips it from the whole source; `preserve` never removes.","default":"auto"}},"additionalProperties":false,"type":"object","required":["image_url"],"title":"BannersGenerationRequest","description":"Header artwork for every DSP banner shape.\n\nA source narrower than a banner's canvas is extended generatively rather\nthan letterboxed, so a square cover still fills a wide header."},"ErrorResponse":{"properties":{"error":{"type":"string","title":"Error"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["error"],"title":"ErrorResponse"},"InvalidFieldReport":{"properties":{"field":{"type":"string","title":"Field","description":"Dotted path to the offending field, as sent — e.g. `vinyl_color`.","examples":["vinyl_color"]},"message":{"type":"string","title":"Message","description":"What is wrong with it.","examples":["Extra inputs are not permitted"]}},"type":"object","required":["field","message"],"title":"InvalidFieldReport","description":"One field the request body got wrong."},"JobErrorResponse":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"}},"type":"object","required":["code","message"],"title":"JobErrorResponse","description":"Why a job failed, in terms a caller can branch on."},"JobListResponse":{"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobSummaryResponse"},"type":"array","title":"Jobs"},"has_more":{"type":"boolean","title":"Has More","description":"Whether older jobs exist beyond this page."},"next_starting_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Starting After","description":"Pass as `starting_after` for the next page. `null` when this is the last one."}},"type":"object","required":["has_more"],"title":"JobListResponse","description":"One page of an account's jobs, newest first."},"JobOutputResponse":{"properties":{"spec_id":{"type":"string","title":"Spec Id"},"kind":{"type":"string","title":"Kind"},"mime_type":{"type":"string","title":"Mime Type"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height"},"bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bytes"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["spec_id","kind","mime_type"],"title":"JobOutputResponse","description":"One delivered file. ``url`` is signed per read and expires."},"JobResponse":{"properties":{"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled"],"title":"Status"},"asset_type":{"type":"string","title":"Asset Type"},"credits_charged":{"type":"integer","title":"Credits Charged"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"outputs":{"items":{"$ref":"#/components/schemas/JobOutputResponse"},"type":"array","title":"Outputs"},"typography":{"anyOf":[{"$ref":"#/components/schemas/TypographyReport"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/JobErrorResponse"},{"type":"null"}]}},"type":"object","required":["job_id","status","asset_type","credits_charged"],"title":"JobResponse","description":"A generation job. The same shape from submit through to terminal."},"JobSummaryResponse":{"properties":{"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","enum":["queued","running","succeeded","failed","cancelled"],"title":"Status"},"asset_type":{"type":"string","title":"Asset Type"},"credits_charged":{"type":"integer","title":"Credits Charged"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"output_count":{"type":"integer","title":"Output Count","description":"How many files this job delivered. Fetch the job itself for their URLs."},"error":{"anyOf":[{"$ref":"#/components/schemas/JobErrorResponse"},{"type":"null"}]}},"type":"object","required":["job_id","status","asset_type","credits_charged","output_count"],"title":"JobSummaryResponse","description":"A job as it appears in a list: everything but the files.\n\nDeliberately not :class:`JobResponse`. Every `url` on that model is a\nfresh IAM signature, and one page of avatar jobs is hundreds of them — a\nlist that signed would be slow for the many callers reconciling ids\nagainst their own records, to serve the few who want a file. Fetch\n`GET /v1/jobs/{job_id}` for those."},"MotionEstimateOption":{"properties":{"motion":{"type":"string","enum":["ken_burns","split_layer","calm","high","custom_prompt","glitch","camera_shift","dolly_zoom","camera_blur","matrix"],"title":"Motion","description":"How to animate the artwork. `GET /estimate` lists the price of each.","x-enumDescriptions":{"calm":"Calm drift and subtle movement.","camera_blur":"Dreamy focal-point shift.","camera_shift":"Drifts between two camera angles.","custom_prompt":"Motion follows the `prompt` you send. `prompt` is required.","dolly_zoom":"Classic vertigo depth warp.","glitch":"Randomized placement of artwork elements.","high":"Bold, lively motion.","ken_burns":"Smooth zoom and pan.","matrix":"Bullet-time orbit around the subject.","split_layer":"Artwork over a softly moving backdrop."}},"credits":{"type":"integer","title":"Credits"},"credit_type":{"type":"string","title":"Credit Type","description":"Ledger key this would bill against."},"allowed":{"type":"boolean","title":"Allowed","description":"Whether this account's tier can submit this motion."}},"type":"object","required":["motion","credits","credit_type","allowed"],"title":"MotionEstimateOption","description":"What one motion costs on this asset type."},"PartnerIdentityResponse":{"properties":{"account_id":{"type":"string","title":"Account Id","description":"The account this key acts for."},"key_id":{"type":"string","title":"Key Id"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"env":{"type":"string","title":"Env"},"rate_limit_per_min":{"type":"integer","title":"Rate Limit Per Min","description":"Requests per minute this key is allowed."},"tier":{"type":"string","title":"Tier","description":"What this account may generate: `basic` covers still asset types and the `ken_burns` and `split_layer` motions; `full` covers every supported pipeline.","examples":["basic"]}},"type":"object","required":["account_id","key_id","scopes","env","rate_limit_per_min","tier"],"title":"PartnerIdentityResponse","description":"Response for ``GET /v1/me`` — what this key is and what it may do.\n\nThe endpoint a partner hits first to confirm their credential works, and\nthe one we point at when an integration is failing and nobody is sure\nwhether the key or the request is at fault."},"PartnerUsageResponse":{"properties":{"account_id":{"type":"string","title":"Account Id"},"window_days":{"type":"integer","title":"Window Days"},"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id","description":"Set when the summary was narrowed to one key."},"credits_spent":{"type":"integer","title":"Credits Spent","description":"Positive: credits consumed in the window."},"generations":{"type":"integer","title":"Generations"},"credit_balance":{"type":"integer","title":"Credit Balance","description":"The account's balance right now, not at the window's end."},"by_asset_type":{"items":{"$ref":"#/components/schemas/UsageByAssetType"},"type":"array","title":"By Asset Type"},"truncated":{"type":"boolean","title":"Truncated","description":"True when the window held more rows than one response covers; narrow `days` for an exact figure.","default":false}},"type":"object","required":["account_id","window_days","credits_spent","generations","credit_balance"],"title":"PartnerUsageResponse","description":"What this account spent, for reconciling a bill.\n\nSummarised from the same ledger rows the admin panel reads rather than a\npartner-specific counter: a second tally would be a second thing to keep\ncorrect, and the first time the two disagreed nobody would know which was\nright."},"ReelsGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"motion":{"type":"string","enum":["ken_burns","split_layer","calm","high","custom_prompt","glitch","camera_shift","dolly_zoom","camera_blur","matrix"],"title":"Motion","description":"How to animate the artwork. `GET /estimate` lists the price of each.","examples":["high"],"x-enumDescriptions":{"calm":"Calm drift and subtle movement.","camera_blur":"Dreamy focal-point shift.","camera_shift":"Drifts between two camera angles.","custom_prompt":"Motion follows the `prompt` you send. `prompt` is required.","dolly_zoom":"Classic vertigo depth warp.","glitch":"Randomized placement of artwork elements.","high":"Bold, lively motion.","ken_burns":"Smooth zoom and pan.","matrix":"Bullet-time orbit around the subject.","split_layer":"Artwork over a softly moving backdrop."}},"prompt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Prompt","description":"Motion direction. Required when `motion` is `custom_prompt`, and rejected otherwise.","examples":["slow drifting smoke"]}},"additionalProperties":false,"type":"object","required":["image_url","motion"],"title":"ReelsGenerationRequest","description":"A 15-second 9:16 clip for Reels and Shorts."},"SpotifyCanvasGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"motion":{"type":"string","enum":["ken_burns","split_layer","calm","high","custom_prompt","glitch","camera_shift","dolly_zoom","camera_blur","matrix"],"title":"Motion","description":"How to animate the artwork. `GET /estimate` lists the price of each.","examples":["high"],"x-enumDescriptions":{"calm":"Calm drift and subtle movement.","camera_blur":"Dreamy focal-point shift.","camera_shift":"Drifts between two camera angles.","custom_prompt":"Motion follows the `prompt` you send. `prompt` is required.","dolly_zoom":"Classic vertigo depth warp.","glitch":"Randomized placement of artwork elements.","high":"Bold, lively motion.","ken_burns":"Smooth zoom and pan.","matrix":"Bullet-time orbit around the subject.","split_layer":"Artwork over a softly moving backdrop."}},"prompt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Prompt","description":"Motion direction. Required when `motion` is `custom_prompt`, and rejected otherwise.","examples":["slow drifting smoke"]}},"additionalProperties":false,"type":"object","required":["image_url","motion"],"title":"SpotifyCanvasGenerationRequest","description":"A 9:16 looping Spotify Canvas clip, up to 8 seconds."},"StillEstimateResponse":{"properties":{"asset_type":{"type":"string","title":"Asset Type"},"credits":{"type":"integer","title":"Credits"},"credit_type":{"type":"string","title":"Credit Type","description":"Ledger key this would bill against."}},"type":"object","required":["asset_type","credits","credit_type"],"title":"StillEstimateResponse","description":"The one price of a still asset type.\n\nAvatars and banners have nothing that changes the charge: typography and\nthe source artwork are the same credit either way."},"TypographyReport":{"properties":{"requested":{"type":"string","enum":["auto","remove","preserve"],"title":"Requested"},"applied":{"type":"boolean","title":"Applied","description":"Whether any pixels were actually changed."},"provider":{"type":"string","title":"Provider","description":"Which backend cleaned it, or `none`.","examples":["cloudinary"]},"passes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Passes"},"verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Verified","description":"Whether a check confirmed the result is text-free. `null` if it could not run."},"residue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Residue","description":"How damaged the repaired area looks: `none`, `faint` or `obvious`."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why removal was skipped or failed."}},"type":"object","required":["requested","applied","provider"],"title":"TypographyReport","description":"What happened to the artwork's typography.\n\nReported, not just applied. ``provider: \"none\"`` is what lets a caller tell\n\"we cleaned it\" from \"there was nothing applied over the artwork to clean\",\nand removal is best-effort everywhere in this system — so a failure has to\nbe visible here or it is discovered downstream at a DSP.\n\n``verified`` and ``residue`` are orthogonal: a pass can leave no readable\ntext and still look visibly scarred."},"UsageByAssetType":{"properties":{"asset_type":{"type":"string","title":"Asset Type"},"credits":{"type":"integer","title":"Credits"},"generations":{"type":"integer","title":"Generations"}},"type":"object","required":["asset_type","credits","generations"],"title":"UsageByAssetType","description":"What one asset type accounted for in the window."},"ValidationErrorResponse":{"properties":{"error":{"type":"string","const":"invalid_request","title":"Error","default":"invalid_request"},"message":{"type":"string","title":"Message","description":"Every field problem in one line, for logs and humans.","examples":["vinyl_color: Extra inputs are not permitted"]},"fields":{"items":{"$ref":"#/components/schemas/InvalidFieldReport"},"type":"array","title":"Fields","description":"One entry per offending field."}},"type":"object","required":["message","fields"],"title":"ValidationErrorResponse","description":"A `422`: the request did not match this endpoint's schema.\n\nDistinct from the `400`s, which are rules about the *combination* of\notherwise-valid fields. This one means a field is missing, malformed, or\ndoes not exist on this endpoint at all — the last being the common one,\nsince each endpoint accepts only the fields that apply to it.\n\nShaped like every other error on this surface so a caller can branch on\n`error` without special-casing validation, with `fields` added to say\nwhich parts of the body to fix."},"VideoEstimateResponse":{"properties":{"asset_type":{"type":"string","title":"Asset Type"},"options":{"items":{"$ref":"#/components/schemas/MotionEstimateOption"},"type":"array","title":"Options"}},"type":"object","required":["asset_type","options"],"title":"VideoEstimateResponse","description":"A price per motion. The prompt, and a visualizer's audio track, do not change the charge."},"VinylEstimateOption":{"properties":{"back_cover":{"type":"boolean","title":"Back Cover","description":"`false` is the front cover alone. `true` is a front plus `back_image_url` on submit, which is a second render. `appearance=sleeve12` has no back cover, so only `false` applies to it."},"credits":{"type":"integer","title":"Credits"},"credit_type":{"type":"string","title":"Credit Type","description":"Ledger key this would bill against."}},"type":"object","required":["back_cover","credits","credit_type"],"title":"VinylEstimateOption","description":"One vinyl price. A back cover is the only option that changes it."},"VinylEstimateResponse":{"properties":{"asset_type":{"type":"string","title":"Asset Type"},"options":{"items":{"$ref":"#/components/schemas/VinylEstimateOption"},"type":"array","title":"Options"}},"type":"object","required":["asset_type","options"],"title":"VinylEstimateResponse","description":"Both vinyl prices. Color, labels and appearance do not change the charge."},"VinylGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"appearance":{"type":"string","enum":["lp","sleeve12"],"title":"Appearance","description":"Physical-media mockup to render. `lp` is a 12\" LP sleeve; `sleeve12` shows the record pulled partway out with its label exposed. `sleeve12` has no back cover.","default":"lp"},"back_image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Back Image Url","description":"HTTPS URL of the back cover. `lp` only — bills a second credit for the extra deliverable. Rejected for `sleeve12`, which has no back.","examples":["https://cdn.label.example/back.jpg"]},"vinyl_color":{"anyOf":[{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}],"title":"Vinyl Color","description":"Exposed record color as `#RRGGBB`. Omit for the default opaque black.","examples":["#CC1414"]},"show_label":{"type":"boolean","title":"Show Label","description":"Expose the record with its label showing, cover-fit from the front cover art. Default is a fully sealed sleeve with no exposed record. Always on for `sleeve12`, which has no sealed appearance.","default":false},"label_image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Label Image Url","description":"HTTPS URL of custom center-label art for the front side, cover-fit into the label disc in place of the front cover art. Requires the front side to actually show a label — `show_label=True`, or `appearance='sleeve12'` where it always does.","examples":["https://cdn.label.example/side-a-label.jpg"]},"back_label_image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Back Label Image Url","description":"HTTPS URL of custom center-label art for the back side. `lp` only, and requires both `back_image_url` (nothing to attach a back label to otherwise) and a shown label (`show_label=True`).","examples":["https://cdn.label.example/side-b-label.jpg"]}},"additionalProperties":false,"type":"object","required":["image_url"],"title":"VinylGenerationRequest","description":"A physical-media mockup: the cover composited onto a record sleeve.\n\nTakes no `motion` and no `typography` — this composites the artwork into a\nfixed template rather than reframing it to a spec that might forbid text,\nso there is nothing for typography handling to decide."},"YoutubeVisualizerGenerationRequest":{"properties":{"image_url":{"type":"string","maxLength":2048,"title":"Image Url","description":"HTTPS URL of the source artwork. Must resolve to a public address.","examples":["https://cdn.label.example/art.jpg"]},"webhook_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Webhook Url","description":"HTTPS endpoint notified once the job reaches a terminal state."},"motion":{"type":"string","enum":["ken_burns","split_layer","calm","high","custom_prompt","glitch","camera_shift","dolly_zoom","camera_blur","matrix"],"title":"Motion","description":"How to animate the artwork. `GET /estimate` lists the price of each.","examples":["high"],"x-enumDescriptions":{"calm":"Calm drift and subtle movement.","camera_blur":"Dreamy focal-point shift.","camera_shift":"Drifts between two camera angles.","custom_prompt":"Motion follows the `prompt` you send. `prompt` is required.","dolly_zoom":"Classic vertigo depth warp.","glitch":"Randomized placement of artwork elements.","high":"Bold, lively motion.","ken_burns":"Smooth zoom and pan.","matrix":"Bullet-time orbit around the subject.","split_layer":"Artwork over a softly moving backdrop."}},"prompt":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Prompt","description":"Motion direction. Required when `motion` is `custom_prompt`, and rejected otherwise.","examples":["slow drifting smoke"]},"audio_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Audio Url","description":"HTTPS URL of an audio bed. Capped at 50 MB / 8 minutes (480s). When supplied, the delivered video is looped and muxed to that track's own length.","examples":["https://cdn.label.example/track.mp3"]}},"additionalProperties":false,"type":"object","required":["image_url","motion"],"title":"YoutubeVisualizerGenerationRequest","description":"A 16:9 visualizer clip, about 8 to 12 seconds, optionally muxed to a track."}},"securitySchemes":{"ApiKey":{"type":"http","description":"An API key issued from your CoverScale account.","scheme":"bearer"}}},"tags":[{"name":"Account","description":"Who this key is, what it may do, and what this account has spent."},{"name":"Generations","description":"Submit a generation, or price one without charging."},{"name":"Jobs","description":"Poll a job, or ask it to stop."}]}