{"openapi":"3.1.0","info":{"title":"AdChef AI Creative API","version":"1.0.0","description":"# Creative API v1\n\nGenerate production-ready video, static, carousel, and rich-media ads — and adapt a finished static ad into new sizes — with a conventional JSON REST API. Use polling, or supply a webhook URL for durable terminal callbacks.\n\n## Quickstart\n\n1. Create an API key in **Settings → API Keys** and send it as `Authorization: Bearer agz_live_…`.\n2. Call `POST /api/v1/videos` with the creative options plus **one** brand reference: an existing `brand_id`, or `brand_name` + `website` together. With the website form, an existing brand is reused when its website matches; otherwise the brand is created and scraped automatically.\n3. Video requests auto-render by default. Poll `GET /api/v1/generations/video/{id}` using each status response's `poll_after_seconds` hint (60s for static/carousel/rich media, 300s for video), or supply `webhook_url` on creation.\n4. For video, stop polling on `rendered` — that is the status carrying `video_url` on status and result payloads. Also stop on `completed` (the composition is authored but no MP4 was requested or the render failed — check `error_message`) and on `failed`. Set `auto_render: false` only for an edit-first flow that calls the render endpoint later; such a video settles at `completed` until you do.\n\nAll responses use `{ \"ok\": true, \"data\": ... }` or `{ \"ok\": false, \"error\": { \"code\", \"message\", \"details\"? } }`. Create endpoints return HTTP 202; reads return HTTP 200.\n\n## Operational behavior\n\n- Plan: this REST API is included with the **Enterprise** plan. Every other plan authenticates fine but receives HTTP 403 `ENTITLEMENT_REQUIRED` here — use the MCP server, which is included with every plan.\n- Scopes: `creative:generate` for creates/renders/cancels, `creative:read` for generation reads, `brand:read` for brand reads, and `credits:read` for balances.\n- Rate limit: 60 requests per minute per authenticated connection (API key, or OAuth client+user). HTTP 429 includes `Retry-After`.\n- Idempotency: send a unique `Idempotency-Key` header on create requests. The JSON `idempotency_key` field is also accepted; the header wins. Successful responses are replayable for about 24 hours.\n- Credits and plan: create endpoints enforce the subscription gate and then the live balance. No speculative pre-check is needed. HTTP 402 carries one of two error codes, each with its own `details`: `SUBSCRIPTION_REQUIRED` (the team has no active plan) includes `billing_url`; `INSUFFICIENT_CREDITS` includes `required_credits` and `balance` (the live balance). Feature gates answer HTTP 403 `ENTITLEMENT_REQUIRED` with `details.required_entitlement`: video creation needs the `videoGeneration` entitlement (included on every paid tier) in every `agent_mode` that generates video clips; `agent_mode: image-only` produces no generated video and passes with an active plan and credits alone. The `videoGeneration` gate does not restrict rendering, reading or cancelling existing videos — but the REST API's own Enterprise entitlement (above) still applies to every REST call, those included. Video render size: a render that omits `resolution` takes the best size the team's plan allows — `\"4k\"` (twice the video's native size) on the Pro plan and above, which includes every plan that can call this REST API, otherwise `\"1080p\"` — and is never refused for its size. Send `resolution: \"1080p\"` to force the video's native size. An explicit `resolution: \"4k\"` requires the Pro plan or higher (the `videoExport4k` entitlement).\n- Status lifecycle: `queued → scraping → generating → completed | partial | failed`. Video adds a render leg: `completed → rendering → rendered`, where `completed` means the composition is authored, `rendering` means an MP4 render is genuinely in flight, and `rendered` is the only status that carries `video_url`. `rendering` never appears for image, carousel, or rich-media generations.\n- Video terminal states: `rendered` (MP4 ready), `completed` (authored, no MP4 — either `auto_render: false`, or a render that failed, in which case `error_message` explains it), and `failed` (authoring itself failed). A `completed` video can be moved to `rendered` at any time by calling the render endpoint.\n- Attribution: `external_actor_id` is an opaque identifier for your end user. It is echoed on generation reads and never changes authorization or billing tenancy.\n- Webhooks: every create endpoint except `POST /api/v1/videos/cutdowns` accepts `webhook_url` and optional `webhook_secret`. Deliveries are retried, signed with HMAC-SHA256 when a secret is supplied, and may arrive more than once; deduplicate on `delivery_id`. Use `occurred_at` to order lifecycle events when delivery retries or delays make arrival order differ from occurrence order.\n- Strict inputs: both the query string and the JSON body are closed. An undocumented or misspelled query parameter or body field is rejected with HTTP 400 `VALIDATION_ERROR` naming it in `details.issues[].path` — it is never silently ignored, so a typo'd filter can never return an unfiltered list. A documented query parameter sent more than once is rejected the same way rather than silently keeping only the first value.\n- Pagination: list endpoints return an opaque `next_cursor`. Pass it back unchanged; it carries the original filters.\n- Local image or video bytes: `POST /api/v1/uploads` returns a signed `upload_url` plus a durable `asset_url` and `asset_id`. `PUT` the bytes to `upload_url`, then send an image's `asset_url` as a `reference_images[].url` on static ads, carousels, rich media, videos, or `POST /api/v1/static-ads/adapt`, or as the `source_image_url` of `POST /api/v1/static-ads/adapt` — you never need to host the file yourself. Images: JPEG, PNG, WebP or GIF up to 30 MB. Videos: MP4, MOV or WebM up to 200 MB; send the `asset_id` as a `reference_videos[].asset_id` or `footage_videos[].asset_id` on `POST /api/v1/videos`. Uploads are retained for 30 days.\n- Video inputs: `POST /api/v1/videos` accepts `reference_videos` (style/tone references such as winning ads — each an `asset_id` or a public HTTPS `url`, YouTube included; up to 10, the first 3 analyzed in depth) and `footage_videos` (real clips cut into the ad — `asset_id` only, up to 10). Library assets must belong to your account and to the video's brand or no brand; anything else answers `NOT_FOUND`. An uploaded video is verified (real type and size) when first used, so `PUT` its bytes before creating the video.\n- Performance insights: every create endpoint (videos, static ads, carousels, rich media) accepts an optional `insights` string — performance learnings from your analytics (what worked / what didn't), e.g. `hook in first 1.5s wins; UGC testimonials beat studio`. Text today, structured results later. It is appended to `additional_direction` under a \"Performance insights\" heading; the combined text must stay within 20,000 characters, or the request is rejected with HTTP 400 `VALIDATION_ERROR` on `insights` (never truncated).\n- Cutting down your own video: `POST /api/v1/videos/cutdowns` cuts a video you supply — an uploaded `asset_id`, a public HTTPS `url` (YouTube refused), or a rendered `video_id` — into exact trims of its ORIGINAL footage and audio, one per `targets[]` entry (`duration` 6, 15, 30 or 60 seconds × `aspect_ratio` 9:16, 1:1, 4:5 or 16:9; duplicates merged, at most 9 distinct). Sources are capped at 10 minutes and 200 MB. The batch needs 600 credits available and the `videoGeneration` entitlement; only the transcription and analysis it runs are billed and renders are free. It returns one `video` generation per cutdown in `generations[]` — poll each until `rendered` or `failed`. Cutdown statuses carry `own_footage` (`role`, `source`, `duration`, `aspect_ratio`, the picked `segments` — a multi-part edit ending on the end card, each with its `framing`, `reason` and any `hold_seconds` — the `content` window, and, for a single contiguous cut, `trim`) and, when one is not or not yet an MP4, a stable `own_footage.failure_reason` such as `source_too_short` (the target is longer than the source). No webhooks: poll.\n- Resizing an existing ad: `POST /api/v1/static-ads/adapt` reformats a finished static ad (a public HTTPS image URL, an upload, or an `assets[].id` from a prior result) into new sizes for 100 credits per size, plus the LLM base fee only when a size needs the layered compose path. It returns a regular `static_ad` generation. Optional `reference_images` (e.g. the brand logo) keep elements such as the logo exact across sizes and never change the cost."},"paths":{"/api/v1/brands":{"get":{"operationId":"list_brands","summary":"List brands","description":"List the brands available to this account (or another team via team_id, from list_teams). Use the returned brand id with get_brand and the create tools. When the user names a brand, pass that name as `query` instead of paging through everything — it is a case-insensitive substring match on the brand name (e.g. \"old spaghetti\" finds \"The Old Spaghetti Factory\"). Pass plain text — `%` and `_` are matched literally, but `*` is still treated as a wildcard by the backend, so never pad the query with `*`. Order with `sort`: name_asc (default), name_desc, created_at_desc (newest first) or created_at_asc. Supports cursor pagination — when paginating, pass cursor alone; it carries the original query, sort and team scope. Items carry team_id/team_name for grouping.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"query","in":"query","required":false,"schema":{"type":"string","minLength":1}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["name_asc","name_desc","created_at_asc","created_at_desc"]}},{"name":"limit","in":"query","required":false,"schema":{"default":20,"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/ListBrandsData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/brands/{id}":{"get":{"operationId":"get_brand","summary":"Get a brand","description":"Get bounded context for one brand: name, website, description, colors, logo, and up to 10 products (title + image) sourced from the scraped website. Use it to write smarter creative briefs before calling a create tool. The returned logo_url and products[].image_url are public HTTPS urls: pass the ones that fit the brief straight through as reference_images[].url on a create_* tool so the ad features the brand's real products instead of invented ones — they need no create_upload and no publish step.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/BrandData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/credits":{"get":{"operationId":"get_credit_balance","summary":"Get credit balance","description":"Get the current credit balance for the connected account, plus a coarse cost reference per generation type. Call this ONLY when the user explicitly asks about credits/affordability, or to explain an INSUFFICIENT_CREDITS error a create tool returned. Do NOT call it as a routine pre-check before creating: the create tools enforce the credit gate themselves and, on failure, return an INSUFFICIENT_CREDITS error carrying both required_credits and the live balance — a speculative balance check before every generation only adds latency.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreditBalanceData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/videos":{"post":{"operationId":"create_video_ad","summary":"Create a video ad","description":"Create an AI video ad generation for a brand (defaults to agent_mode standard-v2, voiceover, and auto-render). Every agent_mode that generates video clips (standard, standard-v2, low-tier, high-tier) requires the videoGeneration entitlement (Pro and above) and fails with ENTITLEMENT_REQUIRED otherwise; image-only produces no generated video and needs only an active plan and credits. Cost scales with BOTH video_length and agent_mode: standard / standard-v2 runs ~400 credits/second at every supported length (2400 at 6s, 3200 at 8s, 4000 at 10s, 6000 at 15s, 12000 at 30s); high-tier is roughly 2x that (up to ~833 credits/second at 30s), low-tier roughly half (down to ~167 credits/second at 30s), and image-only ~1/6 (~67 credits/second). image-only produces no AI-generated video clips — with auto_render (the default) the stills are still assembled into a rendered MP4 slideshow, not real footage; do not pick it when you need actual video. reference_images supplies labeled image context to the video agent. reference_videos (asset_id or public HTTPS/YouTube url, up to 10; the first 3 are analyzed in depth) supplies style/tone reference videos such as winning ads; footage_videos (asset_id only, up to 10) supplies real clips the agent cuts into the video. Upload local video files with create_upload (mp4/mov/webm, up to 200 MB) and pass the returned asset_id. Supply webhook_url for terminal callbacks, or poll get_generation_status. `rendered` means the MP4 is ready and video_url is set; `completed` means the composition is authored with no MP4 yet — either auto_render was false, or a render failed. Stop polling on either, and on failed. Set auto_render=false only for an edit-first flow that calls render_generation manually; such a video settles at completed until you do, firing generation.completed then and generation.rendered once you render it. Pass agent_mode to override the default ('standard-v2'). insights carries performance learnings from your analytics (what worked / what didn't) and steers the agent toward proven patterns; text today, structured results later.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"video_length":{"type":"string","enum":["6","8","10","15","30"],"description":"Video length in seconds."},"aspect_ratio":{"default":"16:9","type":"string","enum":["16:9","9:16","1:1","3:4","4:5","120:31","11:80","320:73"]},"video_objective":{"default":"store_traffic","type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"video_tone":{"default":"autodetect","description":"Tone, e.g. 'dramatic', 'humorous'; 'autodetect' lets the agent pick.","type":"string"},"language":{"default":"ENG_US","type":"string"},"audio_option":{"default":"voiceover","description":"Audio style. 'audioless' generates a silent video: no voiceover, music, dialogue, or clip audio.","type":"string","enum":["voiceover","music","dialogue","audioless"]},"video_model":{"description":"Video generation model; omit for 'auto'.","type":"string","enum":["auto","seedance-lite","seedance-pro","seedance-1-pro-uncensored","seedance-1-fast-uncensored","seedance-fast","seedance-fast-reference","seedance-mini","seedance-mini-reference","seedance-pro-reference","seedance-pro-reference-1080p","seedance-1.5-pro","seedance-2.5","seedance-2.5-reference","runway-gen4-turbo","runway-gen-4.5","veo3-fast","veo3","veo3.1","veo3.1-fast","veo3.1-preview","veo3.1-fast-preview","veo3.1-lite","gemini-omni-flash","kling-v2-1","kling-v2-1-master","kling-v2-5-turbo-pro","kling-v2-6-pro","kling-v3-standard","kling-v3-pro","kling-v3-omni-standard","kling-v3-omni-pro","hailuo-02","hailuo-2.3","hailuo-2.3-fast","hailuo-03","hailuo-03-reference","pixverse-v5","pixverse-v5-6","pixverse-v6","pixverse-c1-transition","sora-2","sora-2-pro","ltx-2.3-pro","ltx-2.3-fast","ltx-2-pro","ltx-2-distilled","kling-o1","kling-o3-standard","kling-o3-reference","grok-imagine-video","grok-imagine-r2v","grok-imagine-video-1.5","grok-imagine-video-1.5-t2v","grok-imagine-video-1.5-r2v","happy-horse","happy-horse-reference","happy-horse-1.1","happy-horse-1.1-reference","wan-2.6-i2v-flash","wan-2.6-i2v","wan-2.7-r2v","wan-3.0","wan-3.0-prime","wan-3.0-r2v","p-video"]},"additional_direction":{"description":"Extra creative direction for the video agent.","type":"string","maxLength":20000},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"auto_render":{"default":true,"description":"Render the authored composition to an MP4 automatically.","type":"boolean"},"agent_mode":{"description":"Video agent mode; omit for 'standard-v2'. Drives quality and credit cost: standard/standard-v2 runs ~400 credits/second at every supported length, 'high-tier' ~2x that, 'low-tier' ~half, 'image-only' ~1/6. 'image-only' produces no AI-generated video clips — with auto_render on, the stills are still assembled into a rendered MP4 slideshow, not real footage — and is the one mode that does not need the videoGeneration entitlement.","type":"string","enum":["image-only","standard","standard-v2","low-tier","high-tier"]},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"reference_videos":{"description":"Style/tone reference videos (e.g. winning ads): each an asset_id or a public HTTPS / YouTube url. Up to 10; the first 3 are analyzed in depth (pacing, shots, hooks), the rest are passed as plain references.","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"asset_id":{"type":"string","minLength":1,"description":"A video in your media library, e.g. the asset_id create_upload returned for a video upload."},"url":{"description":"Public HTTPS video URL. YouTube watch, shorts, embed and youtu.be links are accepted.","type":"string","maxLength":2048,"format":"uri"},"label":{"description":"Short name the video agent sees for this clip.","type":"string","minLength":1,"maxLength":64}},"additionalProperties":false}},"footage_videos":{"description":"Real footage to cut into the video. asset_id only (upload the clip with create_upload first); URLs are rejected here. Up to 10.","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"asset_id":{"type":"string","minLength":1,"description":"A video in your media library, e.g. the asset_id create_upload returned for a video upload."},"label":{"description":"Short name the video agent sees for this clip.","type":"string","minLength":1,"maxLength":64}},"required":["asset_id"],"additionalProperties":false}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_id","video_length"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"video_length":{"type":"string","enum":["6","8","10","15","30"],"description":"Video length in seconds."},"aspect_ratio":{"default":"16:9","type":"string","enum":["16:9","9:16","1:1","3:4","4:5","120:31","11:80","320:73"]},"video_objective":{"default":"store_traffic","type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"video_tone":{"default":"autodetect","description":"Tone, e.g. 'dramatic', 'humorous'; 'autodetect' lets the agent pick.","type":"string"},"language":{"default":"ENG_US","type":"string"},"audio_option":{"default":"voiceover","description":"Audio style. 'audioless' generates a silent video: no voiceover, music, dialogue, or clip audio.","type":"string","enum":["voiceover","music","dialogue","audioless"]},"video_model":{"description":"Video generation model; omit for 'auto'.","type":"string","enum":["auto","seedance-lite","seedance-pro","seedance-1-pro-uncensored","seedance-1-fast-uncensored","seedance-fast","seedance-fast-reference","seedance-mini","seedance-mini-reference","seedance-pro-reference","seedance-pro-reference-1080p","seedance-1.5-pro","seedance-2.5","seedance-2.5-reference","runway-gen4-turbo","runway-gen-4.5","veo3-fast","veo3","veo3.1","veo3.1-fast","veo3.1-preview","veo3.1-fast-preview","veo3.1-lite","gemini-omni-flash","kling-v2-1","kling-v2-1-master","kling-v2-5-turbo-pro","kling-v2-6-pro","kling-v3-standard","kling-v3-pro","kling-v3-omni-standard","kling-v3-omni-pro","hailuo-02","hailuo-2.3","hailuo-2.3-fast","hailuo-03","hailuo-03-reference","pixverse-v5","pixverse-v5-6","pixverse-v6","pixverse-c1-transition","sora-2","sora-2-pro","ltx-2.3-pro","ltx-2.3-fast","ltx-2-pro","ltx-2-distilled","kling-o1","kling-o3-standard","kling-o3-reference","grok-imagine-video","grok-imagine-r2v","grok-imagine-video-1.5","grok-imagine-video-1.5-t2v","grok-imagine-video-1.5-r2v","happy-horse","happy-horse-reference","happy-horse-1.1","happy-horse-1.1-reference","wan-2.6-i2v-flash","wan-2.6-i2v","wan-2.7-r2v","wan-3.0","wan-3.0-prime","wan-3.0-r2v","p-video"]},"additional_direction":{"description":"Extra creative direction for the video agent.","type":"string","maxLength":20000},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"auto_render":{"default":true,"description":"Render the authored composition to an MP4 automatically.","type":"boolean"},"agent_mode":{"description":"Video agent mode; omit for 'standard-v2'. Drives quality and credit cost: standard/standard-v2 runs ~400 credits/second at every supported length, 'high-tier' ~2x that, 'low-tier' ~half, 'image-only' ~1/6. 'image-only' produces no AI-generated video clips — with auto_render on, the stills are still assembled into a rendered MP4 slideshow, not real footage — and is the one mode that does not need the videoGeneration entitlement.","type":"string","enum":["image-only","standard","standard-v2","low-tier","high-tier"]},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"reference_videos":{"description":"Style/tone reference videos (e.g. winning ads): each an asset_id or a public HTTPS / YouTube url. Up to 10; the first 3 are analyzed in depth (pacing, shots, hooks), the rest are passed as plain references.","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"asset_id":{"type":"string","minLength":1,"description":"A video in your media library, e.g. the asset_id create_upload returned for a video upload."},"url":{"description":"Public HTTPS video URL. YouTube watch, shorts, embed and youtu.be links are accepted.","type":"string","maxLength":2048,"format":"uri"},"label":{"description":"Short name the video agent sees for this clip.","type":"string","minLength":1,"maxLength":64}},"additionalProperties":false}},"footage_videos":{"description":"Real footage to cut into the video. asset_id only (upload the clip with create_upload first); URLs are rejected here. Up to 10.","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"asset_id":{"type":"string","minLength":1,"description":"A video in your media library, e.g. the asset_id create_upload returned for a video upload."},"label":{"description":"Short name the video agent sees for this clip.","type":"string","minLength":1,"maxLength":64}},"required":["asset_id"],"additionalProperties":false}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_name","website","video_length"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateVideoAdData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/videos/cutdowns":{"post":{"operationId":"create_video_cutdowns","summary":"Cut down your own video into exact trims","description":"Cut a video the caller supplies into exact trims — one MP4 per (duration, aspect_ratio) target, cut from the ORIGINAL footage with its ORIGINAL audio. Nothing is generated: an LLM only picks where to cut (a multi-part edit cut on dialog, action and music that ends on the end card), snapped to sentence and shot boundaries; each part is cropped to the aspect ratio, or fitted over a blurred or flat background for text shots and title/end cards. Pass exactly one source: asset_id (upload a local file with create_upload first: mp4/mov/webm, up to 200 MB; PUT the bytes before calling this), url (a public HTTPS video file; YouTube is refused), or video_id (a rendered AdGreetz video of this brand). The source may be at most 10 minutes long. targets: 6, 15, 30 or 60 seconds at 9:16, 1:1, 4:5, 16:9; duplicates are merged, at most 9 distinct. The batch needs 600 credits available (a gate, not the charge: only the transcription and analysis actually run are billed; renders are free) and the videoGeneration entitlement. Returns one video generation per cutdown: poll each with get_generation_status until rendered (MP4 ready) or failed. A target longer than the source is skipped: up front in skipped[] when the source length is already known (video_id), otherwise its generation fails with own_footage.failure_reason source_too_short. The imported source itself appears as source.generation (status completed, no MP4 of its own). No webhooks: poll.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"asset_id":{"description":"A video asset_id from create_upload (or the brand library). Exactly one of asset_id, url or video_id.","type":"string"},"url":{"description":"A public HTTPS link to a video file (mp4/mov/webm, up to 200 MB). YouTube links are refused. Exactly one of asset_id, url or video_id.","type":"string","minLength":1,"maxLength":2048},"video_id":{"description":"A rendered AdGreetz video (a generation.id of type video) of this brand. Exactly one of asset_id, url or video_id.","type":"string"},"targets":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"duration":{"type":"number","enum":[6,15,30,60],"description":"Cutdown length in seconds: 6, 15, 30 or 60."},"aspect_ratio":{"type":"string","enum":["9:16","1:1","4:5","16:9"]}},"required":["duration","aspect_ratio"],"additionalProperties":false},"description":"The cutdowns to make, one per (duration, aspect_ratio); duplicates are merged, at most 9 distinct."}},"required":["brand_id","targets"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"asset_id":{"description":"A video asset_id from create_upload (or the brand library). Exactly one of asset_id, url or video_id.","type":"string"},"url":{"description":"A public HTTPS link to a video file (mp4/mov/webm, up to 200 MB). YouTube links are refused. Exactly one of asset_id, url or video_id.","type":"string","minLength":1,"maxLength":2048},"video_id":{"description":"A rendered AdGreetz video (a generation.id of type video) of this brand. Exactly one of asset_id, url or video_id.","type":"string"},"targets":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"duration":{"type":"number","enum":[6,15,30,60],"description":"Cutdown length in seconds: 6, 15, 30 or 60."},"aspect_ratio":{"type":"string","enum":["9:16","1:1","4:5","16:9"]}},"required":["duration","aspect_ratio"],"additionalProperties":false},"description":"The cutdowns to make, one per (duration, aspect_ratio); duplicates are merged, at most 9 distinct."}},"required":["brand_name","website","targets"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateVideoCutdownsData"}}},"examples":{"queued":{"summary":"Two exact-trim cutdowns of an uploaded video queued","value":{"ok":true,"data":{"brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","source":{"generation":{"type":"video","id":"c0ffee00-2222-4333-8444-5d6e7f8a9b0c"},"imported":true,"app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/c0ffee00-2222-4333-8444-5d6e7f8a9b0c"},"generations":[{"generation":{"type":"video","id":"c0ffee01-2222-4333-8444-5d6e7f8a9b0c"},"name":"spring-promo (15s 9:16)","duration":15,"aspect_ratio":"9:16","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/c0ffee01-2222-4333-8444-5d6e7f8a9b0c","status":"queued"},{"generation":{"type":"video","id":"c0ffee02-2222-4333-8444-5d6e7f8a9b0c"},"name":"spring-promo (15s 1:1)","duration":15,"aspect_ratio":"1:1","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/c0ffee02-2222-4333-8444-5d6e7f8a9b0c","status":"queued"}],"skipped":[],"required_credits":600,"status":"queued"}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/static-ads":{"post":{"operationId":"create_static_ad","summary":"Create a static ad","description":"Create a static ad generation for a brand. Cost: 100 credits per image (quantity × aspect_ratios) plus an LLM base fee. Returns immediately with a generation ref. reference_images supplies labeled image context (product photos, logos, style refs) to the image agent. Supply webhook_url for terminal callbacks, or poll get_generation_status until completed. insights carries performance learnings from your analytics (what worked / what didn't) and steers the agent toward proven patterns; text today, structured results later.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"quantity":{"default":1,"description":"Images per aspect ratio (1-3).","type":"integer","minimum":1,"maximum":3},"aspect_ratios":{"default":["4:5"],"description":"Aspect ratios / IAB sizes to generate (max 6, no duplicates).","minItems":1,"maxItems":6,"type":"array","items":{"type":"string","enum":["1:1","16:9","9:16","4:5","4:3","3:4","3:2","2:3","5:4","21:9","4:1","1:4","8:1","1:8","iab-300x250","iab-336x280","iab-728x90","iab-970x90","iab-970x250","iab-468x60","iab-160x600","iab-120x600","iab-300x600","iab-300x1050","iab-250x250","iab-320x50","iab-320x100","iab-640x100","iab-320x480","iab-580x400","tv-1920x495"]}},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","benefit","bold_typography","camera_roll_quad","candid","comparison","conceptual","doodle_overlay","dynamic_handoff","essentials","humblebrag_apology","hypermotion","influencer_ugc","ingredient","material_typography","miniature_diorama","narrative_still_life","pas_grid","problem_solution","problem_striker","receipt","shelfie","software_apps","stickerbomb","testimonial","text_thread","tone_on_tone","ugly","web_search","zine_style"]},"language":{"default":"English","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"image_model":{"default":"auto","description":"Image model for the run. Defaults to 'auto', which picks a model per requested size subject to that model's capabilities: Nano Banana 2 (1K) for standard aspect ratios and for every size in an RTL language (Hebrew/Arabic), Seedream 5 Pro for exact pixel canvases (IAB/TV presets) in LTR runs. Any size the resolved model cannot draw directly is composed layer-based instead of failing. Naming a specific model pins every size to it.","type":"string","enum":["auto","gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"llm_model":{"description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default.","type":"string"},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_id","ad_objective"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"quantity":{"default":1,"description":"Images per aspect ratio (1-3).","type":"integer","minimum":1,"maximum":3},"aspect_ratios":{"default":["4:5"],"description":"Aspect ratios / IAB sizes to generate (max 6, no duplicates).","minItems":1,"maxItems":6,"type":"array","items":{"type":"string","enum":["1:1","16:9","9:16","4:5","4:3","3:4","3:2","2:3","5:4","21:9","4:1","1:4","8:1","1:8","iab-300x250","iab-336x280","iab-728x90","iab-970x90","iab-970x250","iab-468x60","iab-160x600","iab-120x600","iab-300x600","iab-300x1050","iab-250x250","iab-320x50","iab-320x100","iab-640x100","iab-320x480","iab-580x400","tv-1920x495"]}},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","benefit","bold_typography","camera_roll_quad","candid","comparison","conceptual","doodle_overlay","dynamic_handoff","essentials","humblebrag_apology","hypermotion","influencer_ugc","ingredient","material_typography","miniature_diorama","narrative_still_life","pas_grid","problem_solution","problem_striker","receipt","shelfie","software_apps","stickerbomb","testimonial","text_thread","tone_on_tone","ugly","web_search","zine_style"]},"language":{"default":"English","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"image_model":{"default":"auto","description":"Image model for the run. Defaults to 'auto', which picks a model per requested size subject to that model's capabilities: Nano Banana 2 (1K) for standard aspect ratios and for every size in an RTL language (Hebrew/Arabic), Seedream 5 Pro for exact pixel canvases (IAB/TV presets) in LTR runs. Any size the resolved model cannot draw directly is composed layer-based instead of failing. Naming a specific model pins every size to it.","type":"string","enum":["auto","gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"llm_model":{"description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default.","type":"string"},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_name","website","ad_objective"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateStaticAdData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/static-ads/adapt":{"post":{"operationId":"adapt_static_ad","summary":"Adapt a static ad to new sizes","description":"Adapt a finished static ad into new sizes for a brand (reformat, not redesign). Source is ONE of: source_image_url (a public HTTPS image, or a create_upload asset_url) or source_media_id (an image already in this account, e.g. an assets[].id from get_generation_result). Cost: 100 credits per size when every size is a direct image-to-image reformat; when any size needs the layered compose path (most IAB banners), the request also carries the 800-credit LLM base fee — e.g. 1:1 + iab-728x90 = 1000. required_credits in the response is that pre-authorization estimate (the amount the credit gate checks); the final charge follows the image model's catalog price per generated image and is reported as credits_used on the result. The response lists each size with the path it resolved to. image_model is a preference: it is coerced for an RTL source language and each size is served direct or layered by the shared ratio resolver. reference_images supplies labeled image context (e.g. the brand logo, so every adapted size carries the right mark); it is context only and does not change the cost. Returns immediately with a static_ad generation ref. Supply webhook_url for terminal callbacks, or poll get_generation_status until completed.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"source_image_url":{"description":"Public HTTPS URL of the finished ad to adapt (jpeg/png/webp/gif, up to 25 MB; animated GIFs use their first frame). Accepts a create_upload asset_url. Mutually exclusive with source_media_id.","type":"string","format":"uri"},"source_media_id":{"description":"Id of an image already in this account — e.g. an assets[].id from get_generation_result. Mutually exclusive with source_image_url.","type":"string","minLength":1},"sizes":{"minItems":1,"maxItems":10,"type":"array","items":{"anyOf":[{"type":"string","enum":["1:1","16:9","9:16","4:5","4:3","3:4","3:2","2:3","5:4","21:9","4:1","1:4","8:1","1:8","iab-300x250","iab-336x280","iab-728x90","iab-970x90","iab-970x250","iab-468x60","iab-160x600","iab-120x600","iab-300x600","iab-300x1050","iab-250x250","iab-320x50","iab-320x100","iab-640x100","iab-320x480","iab-580x400","tv-1920x495"]},{"type":"string","pattern":"^custom-(\\d+)x(\\d+)$"}]},"description":"Target sizes (1-10, no duplicates): aspect ratios such as \"1:1\" or \"9:16\", IAB/TV presets such as \"iab-728x90\", or \"custom-{width}x{height}\" with each side 50-4096px and at most 10,000,000 px² in total."},"additional_direction":{"type":"string","maxLength":2000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"image_model":{"default":"gemini-nano-banana-2-1","description":"Preferred image model. Subject to the adaptation resolution rules: coerced for RTL source language, and each size is served direct or layered per the shared ratio resolver.","type":"string","enum":["auto","gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_id","sizes"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"source_image_url":{"description":"Public HTTPS URL of the finished ad to adapt (jpeg/png/webp/gif, up to 25 MB; animated GIFs use their first frame). Accepts a create_upload asset_url. Mutually exclusive with source_media_id.","type":"string","format":"uri"},"source_media_id":{"description":"Id of an image already in this account — e.g. an assets[].id from get_generation_result. Mutually exclusive with source_image_url.","type":"string","minLength":1},"sizes":{"minItems":1,"maxItems":10,"type":"array","items":{"anyOf":[{"type":"string","enum":["1:1","16:9","9:16","4:5","4:3","3:4","3:2","2:3","5:4","21:9","4:1","1:4","8:1","1:8","iab-300x250","iab-336x280","iab-728x90","iab-970x90","iab-970x250","iab-468x60","iab-160x600","iab-120x600","iab-300x600","iab-300x1050","iab-250x250","iab-320x50","iab-320x100","iab-640x100","iab-320x480","iab-580x400","tv-1920x495"]},{"type":"string","pattern":"^custom-(\\d+)x(\\d+)$"}]},"description":"Target sizes (1-10, no duplicates): aspect ratios such as \"1:1\" or \"9:16\", IAB/TV presets such as \"iab-728x90\", or \"custom-{width}x{height}\" with each side 50-4096px and at most 10,000,000 px² in total."},"additional_direction":{"type":"string","maxLength":2000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"image_model":{"default":"gemini-nano-banana-2-1","description":"Preferred image model. Subject to the adaptation resolution rules: coerced for RTL source language, and each size is served direct or layered per the shared ratio resolver.","type":"string","enum":["auto","gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_name","website","sizes"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/AdaptStaticAdData"}}},"examples":{"queued":{"summary":"Adaptation queued — one direct and one layered size","value":{"ok":true,"data":{"generation":{"type":"static_ad","id":"c7d8e9f0-5555-4666-8777-2b3c4d5e6f7a"},"brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","name":"Acme - Adapted - 9/15/2026","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/c7d8e9f0-5555-4666-8777-2b3c4d5e6f7a","source_media_id":"b2c3d4e5-2222-4333-8444-5f6a7b8c9d0e","sizes":[{"size":"1:1","path":"direct"},{"size":"iab-728x90","path":"layered"}],"required_credits":1000,"status":"queued"}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/carousels":{"post":{"operationId":"create_carousel_ad","summary":"Create a carousel ad","description":"Create a carousel ad generation for a brand. Cost: per-panel image credits × panel count + LLM base fee. In auto_panels mode credits are gated at the maximum panel count for the aspect ratio (1:1→12, 3:4→8, 9:16→6), so the estimate is a worst-case ceiling. reference_images supplies labeled image context to the carousel agent. Poll get_generation_status until completed, or supply webhook_url for a terminal callback. insights carries performance learnings from your analytics (what worked / what didn't) and steers the agent toward proven patterns; text today, structured results later.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"num_slides":{"type":"integer","minimum":3,"maximum":12,"description":"Panel count (manual mode) or fallback/cap (auto mode)."},"auto_panels":{"default":true,"description":"When true the planning LLM picks the panel count; credits are gated at the MAX slides for the chosen ratio, so the estimate can exceed num_slides.","type":"boolean"},"slide_aspect_ratio":{"type":"string","enum":["1:1","3:4","9:16"]},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","benefit","essentials","ingredient","panorama","pas","problem_solution","testimonial","type_driven_minimal","corporate_pop","vector_editorial"]},"language":{"default":"English","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"image_model":{"default":"gemini-nano-banana-2-1","type":"string","enum":["gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"llm_model":{"default":"claude-opus-5-5","description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default (claude-opus-5-5).","type":"string"},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_id","num_slides","slide_aspect_ratio","ad_objective"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"num_slides":{"type":"integer","minimum":3,"maximum":12,"description":"Panel count (manual mode) or fallback/cap (auto mode)."},"auto_panels":{"default":true,"description":"When true the planning LLM picks the panel count; credits are gated at the MAX slides for the chosen ratio, so the estimate can exceed num_slides.","type":"boolean"},"slide_aspect_ratio":{"type":"string","enum":["1:1","3:4","9:16"]},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","benefit","essentials","ingredient","panorama","pas","problem_solution","testimonial","type_driven_minimal","corporate_pop","vector_editorial"]},"language":{"default":"English","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"image_model":{"default":"gemini-nano-banana-2-1","type":"string","enum":["gpt-image-2.5-flare","gpt-image-2.5-sunburst","bytedance-seedream-5-lite","bytedance-seedream-5-pro","bytedance-seedream-4-5","gemini","gemini-pro","gemini-3-1-flash-image","gemini-3-1-flash-image-512","gemini-3-1-flash-image-1k","gemini-3-1-flash-image-4k","gemini-3-1-flash-lite-image","gemini-nano-banana-2-1-1k","gemini-nano-banana-2-1","gemini-nano-banana-2-1-4k","gpt-image-2","flux-2-max","wan-2.7-image","wan-2.7-image-pro","reve-remix","muse-image"]},"llm_model":{"default":"claude-opus-5-5","description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default (claude-opus-5-5).","type":"string"},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_name","website","num_slides","slide_aspect_ratio","ad_objective"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateCarouselAdData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/rich-media":{"post":{"operationId":"create_rich_media_ad","summary":"Create a rich-media ad","description":"Create an animated rich-media (HTML5 banner) ad generation for a brand across the requested IAB sizes (max 6 per request). Cost: 250 credits flat. reference_images supplies labeled image context to the rich-media agent. Poll get_generation_status until completed, or supply webhook_url for a terminal callback. insights carries performance learnings from your analytics (what worked / what didn't) and steers the agent toward proven patterns; text today, structured results later.","tags":["Generation creation"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key for replay-safe create retries. Overrides body idempotency_key.","schema":{"type":"string","minLength":1,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"Existing brand","description":"Reference a brand you already have by `brand_id` (from `GET /api/v1/brands`). Do not send `brand_name` or `website`.","type":"object","properties":{"brand_id":{"description":"Existing brand id (from list_brands). Mutually exclusive with brand_name/website.","type":"string","minLength":1},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"target_sizes":{"minItems":1,"maxItems":6,"type":"array","items":{"type":"string","enum":["300x250","336x280","728x90","468x60","970x90","970x250","160x600","120x600","300x600","300x1050","250x250","580x400","320x50","320x100","300x50","320x480","1080x1920","720x1280","336x600","360x640","1920x495","640x100","440x440"]},"description":"IAB sizes to generate (max 6 per request), e.g. [\"300x250\",\"728x90\"]. Known sizes: 300x250, 336x280, 728x90, 468x60, 970x90, 970x250, 160x600, 120x600, 300x600, 300x1050, 250x250, 580x400, 320x50, 320x100, 300x50, 320x480, 1080x1920, 720x1280, 336x600, 360x640, 1920x495, 640x100, 440x440."},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","modern","minimalist","vibrant","professional","creative","vintage","luxury"]},"ad_skill":{"default":"default","description":"Opt-in \"Ad Style\" creative skill. \"gsap-regular\" applies the hand-authored GSAP Regular motion toolkit; the gamified styles \"gsap-scratcher\", \"gsap-fruit-machine\" and \"gsap-spin-to-win\" build an interactive reveal ad and REQUIRE offer_details; \"parallax-scroller\" builds an interscroller whose layers move with page scroll and accepts only these target_sizes: 1080x1920, 720x1280, 336x600, 360x640, 300x600, 300x1050, 320x480, 580x400, 970x250, 1920x495; \"default\" (the default) keeps standard generation.","type":"string","enum":["default","gsap-regular","gsap-scratcher","gsap-fruit-machine","gsap-spin-to-win","parallax-scroller"]},"offer_details":{"description":"Offer/reward revealed by gamified ad styles (e.g. \"20% off\", \"CODE: FIRST50\"). Required when ad_skill is gsap-scratcher, gsap-fruit-machine or gsap-spin-to-win.","type":"string","maxLength":300},"language":{"default":"English","type":"string"},"model":{"default":"claude-opus-5-5","description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default (claude-opus-5-5).","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_id","target_sizes","ad_objective"],"additionalProperties":false},{"title":"New or matched brand from website","description":"Send `brand_name` and `website` together. An existing brand with the same website is reused; otherwise the brand is created and scraped automatically. Do not send `brand_id`.","type":"object","properties":{"brand_name":{"description":"Brand name for find-or-create. Requires website.","type":"string","minLength":1},"website":{"description":"Brand website URL. Required with brand_name.","type":"string","format":"uri"},"idempotency_key":{"description":"Optional client-supplied key; retrying the same call with the same key returns the original result instead of creating a duplicate generation. Deduped for ~24 hours — do not reuse a key for a logically different request.","type":"string","minLength":1,"maxLength":128},"target_sizes":{"minItems":1,"maxItems":6,"type":"array","items":{"type":"string","enum":["300x250","336x280","728x90","468x60","970x90","970x250","160x600","120x600","300x600","300x1050","250x250","580x400","320x50","320x100","300x50","320x480","1080x1920","720x1280","336x600","360x640","1920x495","640x100","440x440"]},"description":"IAB sizes to generate (max 6 per request), e.g. [\"300x250\",\"728x90\"]. Known sizes: 300x250, 336x280, 728x90, 468x60, 970x90, 970x250, 160x600, 120x600, 300x600, 300x1050, 250x250, 580x400, 320x50, 320x100, 300x50, 320x480, 1080x1920, 720x1280, 336x600, 360x640, 1920x495, 640x100, 440x440."},"ad_objective":{"type":"string","enum":["website_visits","store_traffic","phone_calls","brand_awareness","product_launch","sales_promotion"]},"visual_style":{"default":"autodetect","type":"string","enum":["autodetect","modern","minimalist","vibrant","professional","creative","vintage","luxury"]},"ad_skill":{"default":"default","description":"Opt-in \"Ad Style\" creative skill. \"gsap-regular\" applies the hand-authored GSAP Regular motion toolkit; the gamified styles \"gsap-scratcher\", \"gsap-fruit-machine\" and \"gsap-spin-to-win\" build an interactive reveal ad and REQUIRE offer_details; \"parallax-scroller\" builds an interscroller whose layers move with page scroll and accepts only these target_sizes: 1080x1920, 720x1280, 336x600, 360x640, 300x600, 300x1050, 320x480, 580x400, 970x250, 1920x495; \"default\" (the default) keeps standard generation.","type":"string","enum":["default","gsap-regular","gsap-scratcher","gsap-fruit-machine","gsap-spin-to-win","parallax-scroller"]},"offer_details":{"description":"Offer/reward revealed by gamified ad styles (e.g. \"20% off\", \"CODE: FIRST50\"). Required when ad_skill is gsap-scratcher, gsap-fruit-machine or gsap-spin-to-win.","type":"string","maxLength":300},"language":{"default":"English","type":"string"},"model":{"default":"claude-opus-5-5","description":"LLM that authors the ad. Pass one of these exact ids: gemini-3.1-pro-preview (Gemini 3.1 Pro), gemini-4-argon (Gemini 4 Argon), gemini-3.8-flash (Gemini 3.8 Flash), gemini-3.7-flash (Gemini 3.7 Flash), gemini-3.5-flash-lite (Gemini 3.5 Flash-Lite), gpt-6-astra (GPT-6 Astra), gpt-6.1-sol (GPT-6.1 Sol), gpt-5.6-sol (GPT-5.6 Sol), gpt-5.6-terra (GPT-5.6 Terra), gpt-6-luna (GPT-6 Luna), claude-fable-5-1 (Claude Fable 5.1), claude-opus-5-5 (Claude Opus 5.5), claude-sonnet-5-5 (Claude Sonnet 5.5). Map the user's wording to the matching id (e.g. \"opus 5.5\" → claude-opus-5-5); never invent an id. Omit to use the default (claude-opus-5-5).","type":"string"},"additional_direction":{"type":"string","maxLength":20000},"external_actor_id":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128},"insights":{"description":"Performance learnings from your analytics (what worked / what didn't), e.g. 'hook in the first 1.5s wins; UGC testimonials beat studio; \"free shipping\" CTA +22% CTR'. Text today, structured results later. Appended to additional_direction under a \"Performance insights\" heading; the combined text must stay within 20,000 characters.","type":"string","maxLength":20000},"reference_images":{"description":"Labeled images the generation agent can use as creative context (e.g. product photos, logos, style refs published via publish_attachment or create_upload).","minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64},"url":{"type":"string","format":"uri"}},"required":["label","url"]}},"webhook_url":{"description":"HTTPS callback URL for terminal generation events.","type":"string","maxLength":2048,"format":"uri"},"webhook_secret":{"description":"Secret used to HMAC-sign webhook request bodies.","type":"string","minLength":16,"maxLength":256}},"required":["brand_name","website","target_sizes","ad_objective"],"additionalProperties":false}]}}}},"responses":{"202":{"description":"Generation queued.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateRichMediaAdData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/generations":{"get":{"operationId":"list_generations","summary":"List generations","description":"List this account's (or another team's, via team_id from list_teams) creative generations across all four pipelines (static_ad, carousel, rich_media, video), newest first. Filter by type, brand_id, or unified status. Returns up to `limit` (max 50) status-shaped summaries — each carrying team_id/team_name — and an opaque next_cursor when more pages exist — when paginating, pass cursor alone; it carries the original filters and team scope.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["static_ad","carousel","rich_media","video"]}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"string","minLength":1}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]}},{"name":"external_actor_id","in":"query","required":false,"schema":{"description":"Opaque end-user id from the calling platform, for attribution only (echoed back on reads; not used for auth or credits).","type":"string","minLength":1,"maxLength":128}},{"name":"limit","in":"query","required":false,"schema":{"default":20,"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/ListGenerationsData"}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/generations/{type}/{id}":{"get":{"operationId":"get_generation_status","summary":"Get generation status","description":"Check the status of a creative generation. Returns a unified status (queued | scraping | generating | rendering | completed | rendered | partial | failed) plus the pipeline-native raw_status and, when present, error_message. rendering and rendered are video-only. For a video, `rendered` means the MP4 is ready and video_url is set; `completed` means the composition is authored with no MP4 yet — either auto_render was false, or a render failed. Call render_generation to move a completed video to rendered. Poll again after poll_after_seconds until status is completed, rendered, partial, or failed — all four are terminal, so do not keep polling a completed video expecting a video_url that will never arrive. If completed, rendered, or partial, call get_generation_result to fetch the assets. If failed, the generation is terminal — do not poll further; get_generation_result will return GENERATION_FAILED rather than assets. Once terminal, always share app_url with the user as a clickable link to view or edit the result in the app.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"type","in":"path","required":true,"description":"Generation type: static_ad | carousel | rich_media | video","example":"video","schema":{"type":"string","enum":["static_ad","carousel","rich_media","video"]}},{"name":"id","in":"path","required":true,"description":"Generation id.","example":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/GenerationStatusData"}}},"examples":{"video-rendered":{"summary":"Video with MP4 ready (terminal; carries video_url)","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"name":"Acme Summer Sale 30s","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","status":"rendered","raw_status":"rendered","created_at":"2026-07-15T18:20:04.000Z","completed_at":"2026-07-15T18:35:42.000Z","video_url":"https://media.example/video.mp4","poll_after_seconds":300}}},"video-generating":{"summary":"Video still authoring","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"name":"Acme Summer Sale 30s","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","status":"generating","raw_status":"running","created_at":"2026-07-15T18:20:04.000Z","poll_after_seconds":300}}},"static-ad-completed":{"summary":"Static ad finished","value":{"ok":true,"data":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"name":"Acme Summer Sale statics","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","status":"completed","raw_status":"completed","created_at":"2026-07-15T18:20:04.000Z","completed_at":"2026-07-15T18:24:11.000Z","external_actor_id":"customer_874","poll_after_seconds":60}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/generations/{type}/{id}/result":{"get":{"operationId":"get_generation_result","summary":"Get generation result","description":"Fetch the finished assets of a creative generation. Available once get_generation_status reports completed, rendered, or partial; otherwise returns GENERATION_NOT_READY with the current status and a poll hint. If the generation has failed, returns GENERATION_FAILED (terminal, no poll hint) with error_message when available. Image and bundle URLs are signed and expire after expires_in_seconds. For video, `rendered` means the MP4 is ready and video_url is set; `completed` means the composition is authored with no MP4 yet — either auto_render was false, or a render failed. It reports render.rendered false until then — call render_generation to produce it, unless render.supported is false, in which case only app_url can render it. Every result includes app_url — the in-app page to view and edit the creative (for carousels, whose slides are rendered in-app, app_url is the primary deliverable) — and credits_used, the credits attributed to this generation in the usage log when available. Layer-based static ads and carousels report a render block — when render.rendered is false, call render_generation to materialize deployable JPG files (assets will be empty until then); when render.stale is true the files predate the latest design edit and render_generation will refresh them. Always share app_url with the user as a clickable link to view or edit the result in the app. preview_url (when configured) is a shareable, login-free page to view the creative in a browser — prefer it when giving the user a link they can open without signing in.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"type","in":"path","required":true,"description":"Generation type: static_ad | carousel | rich_media | video","example":"video","schema":{"type":"string","enum":["static_ad","carousel","rich_media","video"]}},{"name":"id","in":"path","required":true,"description":"Generation id.","example":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/GenerationResultData"}}},"examples":{"video-rendered":{"summary":"Video with MP4 ready","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"status":"rendered","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","preview_url":"https://YOUR_HOST/preview/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d?token=SIGNED_TOKEN","credits_used":12000,"video_url":"https://media.example/video.mp4","duration_seconds":30,"aspect_ratio":"9:16","thumbnail_url":"https://media.example/thumbnail.png","render":{"required":true,"rendered":true,"supported":true,"tool":"render_generation"}}}},"video-authored-unrendered":{"summary":"Video authored but not rendered (auto_render: false)","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"status":"completed","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","credits_used":12000,"aspect_ratio":"9:16","render":{"required":true,"rendered":false,"supported":true,"tool":"render_generation"}}}},"static-ad-images":{"summary":"Static ad image assets","value":{"ok":true,"data":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"status":"completed","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","credits_used":400,"assets":[{"id":"b2c3d4e5-2222-4333-8444-5f6a7b8c9d0e","kind":"image","url":"https://media.example/static-ad-1080x1350.png?signature=SIGNED","width":1080,"height":1350,"aspect_ratio":"4:5","metadata":{"size":"1080x1350"}}],"expires_in_seconds":3600,"render":{"required":true,"rendered":true,"stale":false,"tool":"render_generation"}}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/generations/{type}/{id}/render":{"post":{"operationId":"render_generation","summary":"Render a generation","description":"Render the final deliverable of a completed generation. For a layer-based static ad or carousel, this materializes deployable JPGs (idempotent: an unchanged design returns the previously rendered files — pass force: true to re-render; carousels produce one JPG per slide, in slide order, plus the composite; flat image static ads need no rendering and the call is a no-op that returns the existing assets). For a video, this queues the final MP4 render — an ASYNC operation: the call returns immediately with task_queued and a poll_after_seconds hint, and the agent must call get_generation_result again after that interval to get video_url; an already-rendered reuse response includes video_url immediately (pass force: true to re-render an already-rendered video). Rendering is included in the generation cost (no additional credits). Video only: resolution picks the MP4 size. Omit it for the best size your plan allows — \"4k\" (twice the video's native size) on the Pro plan and above, which includes every REST API (Enterprise) caller, \"1080p\" otherwise; an omitted resolution is never refused. \"1080p\" forces the native size on any plan. \"4k\" requires the Pro plan or higher; other plans get ENTITLEMENT_REQUIRED with required_entitlement \"videoExport4k\" — retry with \"1080p\" or without resolution, or upgrade. Without resolution, an existing MP4 is reused when it is at least the size your plan allows (a 1080p MP4 is re-rendered in 4K for a Pro or higher plan); with it, an existing MP4 is reused only at that size. Responses report the MP4's resolution, and its width and height in pixels, when known. Not applicable to rich_media generations. Always share app_url with the user as a clickable link to view the result.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"type","in":"path","required":true,"description":"Generation type: static_ad | carousel | rich_media | video","example":"video","schema":{"type":"string","enum":["static_ad","carousel","video"]}},{"name":"id","in":"path","required":true,"description":"Generation id.","example":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","schema":{"type":"string","minLength":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"force":{"type":"boolean"},"resolution":{"description":"Video only: the MP4 size. Omit it for the best size your plan allows: \"4k\" on the Pro plan and above (Business and Enterprise included, so every REST API caller), \"1080p\" otherwise — an omitted resolution is never refused. \"1080p\" forces the video's native size (1080×1920 for 9:16) on any plan. \"4k\" is twice that in each dimension and requires the Pro plan or higher (`videoExport4k`); other plans get ENTITLEMENT_REQUIRED, never a downgraded file. Omitted, an existing MP4 is reused when it is at least the size your plan allows; given, only an MP4 of exactly that size is reused.","type":"string","enum":["1080p","4k"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/RenderGenerationData"}}},"examples":{"video-render-queued":{"summary":"Video render started","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"status":"rendering","rendered":0,"reused":0,"task_queued":true,"poll_after_seconds":300,"resolution":"4k","width":2160,"height":3840,"app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","message":"Video render started — check get_generation_result after poll_after_seconds."}}},"video-already-rendered":{"summary":"Video already rendered (reused)","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"status":"rendered","rendered":0,"reused":1,"video_url":"https://media.example/video.mp4","resolution":"4k","width":2160,"height":3840,"app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","message":"Video already rendered."}}},"static-carousel-rendered":{"summary":"Static/carousel designs rendered","value":{"ok":true,"data":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"status":"completed","rendered":2,"reused":1,"no_render_needed":false,"app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","assets":[{"id":"b2c3d4e5-2222-4333-8444-5f6a7b8c9d0e","kind":"image","url":"https://media.example/static-ad-1080x1350.png?signature=SIGNED","width":1080,"height":1350,"aspect_ratio":"4:5","metadata":{"size":"1080x1350"}}],"expires_in_seconds":3600}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/generations/{type}/{id}/cancel":{"post":{"operationId":"cancel_generation","summary":"Cancel a generation","description":"Stop an in-flight generation — the undo for a mistaken create_* call. Cancellable while the status is queued, scraping, generating; the generation then settles at failed with raw_status cancelled, and the response is the same status payload get_generation_status returns. Cancelling again is a safe no-op that echoes that payload. A generation that already reached a terminal status (completed, rendered, partial, failed) returns GENERATION_TERMINAL with details.status — there is nothing to stop. A video whose MP4 render is already in flight (rendering) cannot be cancelled: the composition is authored and the render settles on its own. Credits already consumed by completed steps are NOT refunded; cancelling only stops the remaining work from spending more. A registered webhook receives the ordinary generation.failed callback exactly once.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"type","in":"path","required":true,"description":"Generation type: static_ad | carousel | rich_media | video","example":"video","schema":{"type":"string","enum":["static_ad","carousel","rich_media","video"]}},{"name":"id","in":"path","required":true,"description":"Generation id.","example":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/GenerationStatusData"}}},"examples":{"video-cancelled":{"summary":"Video generation cancelled before it finished","value":{"ok":true,"data":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"name":"Acme Summer Sale 30s","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","status":"failed","raw_status":"cancelled","error_message":"Cancelled by caller","created_at":"2026-07-15T18:20:04.000Z","poll_after_seconds":300}}},"static-ad-cancelled":{"summary":"Static ad generation cancelled while queued","value":{"ok":true,"data":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"name":"Acme Summer Sale statics","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","status":"failed","raw_status":"cancelled","error_message":"Cancelled by caller","created_at":"2026-07-15T18:20:04.000Z","completed_at":"2026-07-15T18:21:36.000Z","poll_after_seconds":60}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}},"/api/v1/uploads":{"post":{"operationId":"create_upload","summary":"Create an image or video upload ticket","description":"Host image or video bytes you hold locally so no public hosting of your own is required. Images: pass the returned asset_url as a reference_images[].url on any create_* generation tool (create_static_ad, create_carousel_ad, create_rich_media_ad, create_video_ad). Videos: pass the returned asset_id as a reference_videos[].asset_id or footage_videos[].asset_id on create_video_ad, or as the asset_id of create_video_cutdowns. Returns a short-lived signed upload_url plus the permanent asset_url and asset_id: PUT the raw bytes to upload_url with the declared content_type, then reference the asset (never upload_url) on the create tool. Raster images — image/jpeg, image/png, image/webp or image/gif, up to 30 MB (SVG and AVIF are rejected by the generation tools, so they cannot be uploaded here either) — or videos — video/mp4, video/quicktime (.mov) or video/webm, up to 200 MB. The uploaded bytes are verified server-side (real file type and size) when a video is first used, so PUT them before calling create_video_ad. filename is used only to derive the extension and must agree with content_type. Pass content_hash (sha256 hex of the bytes) to reuse an identical file already stored: the response then carries deduplicated: true with asset_url (and asset_id) only, and there is nothing to PUT. Uploaded assets are retained for 30 days, then retired from the media library, then permanently deleted 30 days after that: a finished generation keeps its delivered output, but re-editing it after retirement (regenerate, adapt, retake, or re-rendering a composition that placed the upload directly on screen) may no longer find the original file — upload it again if you need to revisit an older generation. The upload_url itself expires in 2 hours; call create_upload again if it lapses.","tags":["Resources"],"security":[{"bearerAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":255,"description":"Original file name. Used ONLY to derive/validate the extension; the stored object key is generated server-side."},"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","image/gif","video/mp4","video/quicktime","video/webm"],"description":"MIME type of the bytes you are going to PUT."},"size_bytes":{"type":"integer","exclusiveMinimum":0,"maximum":209715200,"description":"Exact byte length of the file: up to 30 MB for an image, 200 MB for a video."},"brand_id":{"description":"Scope the upload to one of your brands so it lands in that brand library.","type":"string","minLength":1},"content_hash":{"description":"sha256 hex digest of the bytes. Enables dedup: an identical file already stored is returned as-is.","type":"string","pattern":"^[0-9a-f]{64}$"}},"required":["filename","content_type","size_bytes"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"const":true},"data":{"$ref":"#/components/schemas/CreateUploadData"}}},"examples":{"ticket":{"summary":"Upload ticket — PUT the bytes to upload_url","value":{"ok":true,"data":{"upload_url":"https://YOUR_PROJECT.supabase.co/storage/v1/object/upload/sign/media_assets/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/images/V1StGXR8Z5.png?token=SIGNED_UPLOAD_TOKEN","method":"PUT","headers":{"content-type":"image/png","cache-control":"max-age=3600"},"asset_url":"https://YOUR_PROJECT.supabase.co/storage/v1/object/public/media_assets/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/images/V1StGXR8Z5.png","expires_in_seconds":7200,"deduplicated":false}}},"deduplicated":{"summary":"content_hash matched an existing image (nothing to upload)","value":{"ok":true,"data":{"asset_url":"https://YOUR_PROJECT.supabase.co/storage/v1/object/public/media_assets/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/images/V1StGXR8Z5.png","deduplicated":true}}}}}}},"400":{"description":"VALIDATION_ERROR: The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}]}}}},"401":{"description":"UNAUTHORIZED: Authentication is missing or invalid.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}]}}}},"402":{"description":"SUBSCRIPTION_REQUIRED: The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page. INSUFFICIENT_CREDITS: The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}]}}}},"403":{"description":"INVALID_SCOPE: The credential lacks the required scope. ENTITLEMENT_REQUIRED: The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}]}}}},"404":{"description":"BRAND_NOT_FOUND: The brand does not exist in the authenticated account. TEAM_NOT_FOUND: The team does not exist or you are not a member of it. GENERATION_NOT_FOUND: The generation does not exist in the authenticated account. NOT_FOUND: A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}]}}}},"409":{"description":"ENTITLEMENT_UNRESOLVED: The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support. GENERATION_NOT_READY: The generation is not ready for this operation. GENERATION_TERMINAL: The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status. REQUEST_IN_FLIGHT: A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]},{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}]}}}},"410":{"description":"GENERATION_FAILED: The generation reached a terminal failed state.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}]}}}},"429":{"description":"RATE_LIMITED: The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}]}}}},"500":{"description":"INTERNAL_ERROR: An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}]}}}}}}}},"webhooks":{"generationTerminal":{"post":{"summary":"Generation terminal event","description":"Sent for generation.completed, generation.rendered, generation.partial, generation.failed, or generation.render_failed. Deliveries are at-least-once and may arrive out of order; occurred_at records when the event was raised and is preserved across delivery retries. For video, generation.rendered means the MP4 is ready and video_url is populated. generation.completed is sent only when the composition is the final deliverable — i.e. auto_render=false; an auto_render=true video is announced by generation.rendered alone, because authoring is a waypoint rather than a terminal state for it. Statuses delivered here are always terminal (completed / rendered / partial / failed); poll_after_seconds is an artifact of the shared status shape and can be ignored on webhook deliveries.","parameters":[{"name":"X-Adgreetz-Event","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Adgreetz-Delivery-Id","in":"header","required":true,"schema":{"type":"string"}},{"name":"X-Adgreetz-Signature","in":"header","required":false,"description":"sha256=<hex HMAC> when webhook_secret was supplied.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayload"},"examples":{"video-rendered":{"summary":"generation.rendered — MP4 ready","value":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"name":"Acme Summer Sale 30s","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","status":"rendered","raw_status":"rendered","created_at":"2026-07-15T18:20:04.000Z","completed_at":"2026-07-15T18:35:42.000Z","video_url":"https://media.example/video.mp4","poll_after_seconds":300,"event":"generation.rendered","delivery_id":"run_01JJXG4M9Q0V3T8B2N5C7D6E1F","occurred_at":"2026-07-15T18:35:42.123Z","result":{"generation":{"type":"video","id":"a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d"},"status":"rendered","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/video-ai/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d","preview_url":"https://YOUR_HOST/preview/a1b2c3d4-1111-4222-8333-9e8f7a6b5c4d?token=SIGNED_TOKEN","credits_used":12000,"video_url":"https://media.example/video.mp4","duration_seconds":30,"aspect_ratio":"9:16","thumbnail_url":"https://media.example/thumbnail.png","render":{"required":true,"rendered":true,"supported":true,"tool":"render_generation"}}}},"static-ad-completed":{"summary":"generation.completed — static ad finished","value":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"name":"Acme Summer Sale statics","brand_id":"0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","status":"completed","raw_status":"completed","created_at":"2026-07-15T18:20:04.000Z","completed_at":"2026-07-15T18:24:11.000Z","external_actor_id":"customer_874","poll_after_seconds":60,"event":"generation.completed","delivery_id":"run_01JJXG4M9Q0V3T8B2N5C7D6E2G","occurred_at":"2026-07-15T18:24:11.456Z","result":{"generation":{"type":"static_ad","id":"e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f"},"status":"completed","app_url":"https://YOUR_HOST/home/brands/0f9e8d7c-7777-4888-8999-6b5a4c3d2e1f/static-ads/e5f6a7b8-4444-4555-8666-1a2b3c4d5e6f","credits_used":400,"assets":[{"id":"b2c3d4e5-2222-4333-8444-5f6a7b8c9d0e","kind":"image","url":"https://media.example/static-ad-1080x1350.png?signature=SIGNED","width":1080,"height":1350,"aspect_ratio":"4:5","metadata":{"size":"1080x1350"}}],"expires_in_seconds":3600,"render":{"required":true,"rendered":true,"stale":false,"tool":"render_generation"}}}}}}}},"responses":{"200":{"description":"Callback accepted."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"agz_live_… or Supabase OAuth access token","description":"Account API key beginning with agz_live_, or an authorized Supabase OAuth access token."}},"schemas":{"ListBrandsData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"name":{"type":"string"},"website":{"type":["string","null"]},"logo_url":{"type":"string"},"team_id":{"type":"string","minLength":1},"team_name":{"type":"string"}},"required":["id","name","website","team_id"]}},"next_cursor":{"type":"string"}},"required":["items"]},"BrandData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","minLength":1},"name":{"type":"string"},"website":{"type":["string","null"]},"description":{"type":"string"},"colors":{},"logo_url":{"type":"string"},"products":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"image_url":{"type":"string"}},"required":["title"]}}},"required":["id","name","website","products"]},"CreditBalanceData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"balance":{"type":"number"},"cost_reference":{"type":"object","properties":{"static_ad":{"type":"string"},"carousel":{"type":"string"},"rich_media":{"type":"string"},"video":{"type":"string"}},"required":["static_ad","carousel","rich_media","video"]}},"required":["balance","cost_reference"]},"CreateVideoAdData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"video"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"brand_id":{"type":"string","minLength":1},"name":{"type":"string"},"app_url":{"type":"string"},"required_credits":{"type":"number"},"status":{"type":"string","const":"queued"}},"required":["generation","brand_id","name","app_url","required_credits","status"]},"CreateVideoCutdownsData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"brand_id":{"type":"string","minLength":1},"source":{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"video"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"imported":{"type":"boolean","description":"true when the source was imported as a new video row; false for a video_id source."},"app_url":{"type":"string"}},"required":["generation","imported","app_url"]},"generations":{"type":"array","items":{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"video"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"name":{"type":"string"},"duration":{"type":"number"},"aspect_ratio":{"type":"string"},"app_url":{"type":"string"},"status":{"type":"string","const":"queued"}},"required":["generation","name","duration","aspect_ratio","app_url","status"]}},"skipped":{"type":"array","items":{"type":"object","properties":{"duration":{"type":"number"},"aspect_ratio":{"type":"string"},"reason":{"type":"string","const":"source_too_short"}},"required":["duration","aspect_ratio","reason"]}},"required_credits":{"type":"number","description":"The balance the batch had to hold to start (a gate, not the charge): only the transcription and analysis it runs are billed, and renders are free."},"status":{"type":"string","const":"queued"}},"required":["brand_id","source","generations","skipped","required_credits","status"]},"CreateStaticAdData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"static_ad"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"brand_id":{"type":"string","minLength":1},"name":{"type":"string"},"app_url":{"type":"string"},"required_credits":{"type":"number"},"status":{"type":"string","const":"queued"}},"required":["generation","brand_id","name","app_url","required_credits","status"]},"AdaptStaticAdData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"static_ad"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"brand_id":{"type":"string","minLength":1},"name":{"type":"string"},"app_url":{"type":"string"},"required_credits":{"type":"number"},"status":{"type":"string","const":"queued"},"source_media_id":{"type":"string","minLength":1},"sizes":{"type":"array","items":{"type":"object","properties":{"size":{"type":"string","minLength":1},"path":{"type":"string","enum":["direct","layered"]}},"required":["size","path"]}}},"required":["generation","brand_id","name","app_url","required_credits","status","source_media_id","sizes"]},"CreateCarouselAdData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"carousel"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"brand_id":{"type":"string","minLength":1},"name":{"type":"string"},"app_url":{"type":"string"},"required_credits":{"type":"number"},"status":{"type":"string","const":"queued"}},"required":["generation","brand_id","name","app_url","required_credits","status"]},"CreateRichMediaAdData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"rich_media"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"brand_id":{"type":"string","minLength":1},"name":{"type":"string"},"app_url":{"type":"string"},"required_credits":{"type":"number"},"status":{"type":"string","const":"queued"}},"required":["generation","brand_id","name","app_url","required_credits","status"]},"ListGenerationsData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"name":{"type":"string"},"brand_id":{"type":"string","minLength":1},"app_url":{"type":"string"},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"raw_status":{"type":"string"},"error_message":{"type":"string"},"created_at":{"type":"string"},"completed_at":{"type":"string"},"external_actor_id":{"type":"string"},"video_url":{"type":"string"},"own_footage":{"type":"object","properties":{"role":{"type":"string","enum":["source","cutdown"]},"source":{"description":"Cutdowns only: the source video generation it was cut from.","type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"duration":{"description":"Cutdowns only: target seconds.","type":"number"},"aspect_ratio":{"type":"string"},"trim":{"description":"Where in the source the cut starts and ends, once picked — only when it is one contiguous stretch.","type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"}},"required":["start_seconds","end_seconds"]},"segments":{"description":"The parts of the source the cutdown plays, in order (a multi-part edit cut on dialog, action and music, ending on the end card), once picked.","type":"array","items":{"type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"},"framing":{"type":"string","enum":["fill","fit_blur","fit_solid"],"description":"fill crops to the aspect ratio; fit_blur fits the frame over a blurred fill (busy text shots); fit_solid fits it on its own flat background colour (title and end cards)."},"reason":{"description":"Why the part is in the edit.","type":"string","enum":["dialog","action","music","end_card"]},"hold_seconds":{"description":"Seconds the part's last frame is held after it, so the cut lands on its exact length.","type":"number"}},"required":["start_seconds","end_seconds","framing"]}},"content":{"description":"The source's content window the cut was planned in, when a black or silent head or tail was trimmed.","type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"}},"required":["start_seconds","end_seconds"]},"failure_reason":{"type":"string","enum":["source_too_short","source_too_long","insufficient_credits","download_failed","unreadable_video","store_failed","render_not_started","render_failed","cancelled","start_failed","failed"]}},"required":["role"]},"poll_after_seconds":{"type":"number"},"team_id":{"type":"string","minLength":1},"team_name":{"type":"string"}},"required":["generation","name","brand_id","app_url","status","raw_status","created_at","poll_after_seconds","team_id"]}},"next_cursor":{"type":"string"}},"required":["items"]},"GenerationStatusData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"name":{"type":"string"},"brand_id":{"type":"string","minLength":1},"app_url":{"type":"string"},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"raw_status":{"type":"string"},"error_message":{"type":"string"},"created_at":{"type":"string"},"completed_at":{"type":"string"},"external_actor_id":{"type":"string"},"video_url":{"type":"string"},"own_footage":{"type":"object","properties":{"role":{"type":"string","enum":["source","cutdown"]},"source":{"description":"Cutdowns only: the source video generation it was cut from.","type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"duration":{"description":"Cutdowns only: target seconds.","type":"number"},"aspect_ratio":{"type":"string"},"trim":{"description":"Where in the source the cut starts and ends, once picked — only when it is one contiguous stretch.","type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"}},"required":["start_seconds","end_seconds"]},"segments":{"description":"The parts of the source the cutdown plays, in order (a multi-part edit cut on dialog, action and music, ending on the end card), once picked.","type":"array","items":{"type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"},"framing":{"type":"string","enum":["fill","fit_blur","fit_solid"],"description":"fill crops to the aspect ratio; fit_blur fits the frame over a blurred fill (busy text shots); fit_solid fits it on its own flat background colour (title and end cards)."},"reason":{"description":"Why the part is in the edit.","type":"string","enum":["dialog","action","music","end_card"]},"hold_seconds":{"description":"Seconds the part's last frame is held after it, so the cut lands on its exact length.","type":"number"}},"required":["start_seconds","end_seconds","framing"]}},"content":{"description":"The source's content window the cut was planned in, when a black or silent head or tail was trimmed.","type":"object","properties":{"start_seconds":{"type":"number"},"end_seconds":{"type":"number"}},"required":["start_seconds","end_seconds"]},"failure_reason":{"type":"string","enum":["source_too_short","source_too_long","insufficient_credits","download_failed","unreadable_video","store_failed","render_not_started","render_failed","cancelled","start_failed","failed"]}},"required":["role"]},"poll_after_seconds":{"type":"number"}},"required":["generation","name","brand_id","app_url","status","raw_status","created_at","poll_after_seconds"]},"GenerationResultData":{"$schema":"https://json-schema.org/draft/2020-12/schema","anyOf":[{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"external_actor_id":{"type":"string"},"app_url":{"type":"string"},"preview_url":{"type":"string"},"credits_used":{"type":"number"},"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"kind":{"type":"string","const":"image"},"url":{"type":"string"},"width":{"type":"number"},"height":{"type":"number"},"aspect_ratio":{"type":"string"},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["id","kind","url","metadata"]}},"expires_in_seconds":{"type":"number"},"render":{"type":"object","properties":{"required":{"type":"boolean","const":true},"rendered":{"type":"boolean"},"stale":{"type":"boolean"},"tool":{"type":"string","const":"render_generation"}},"required":["required","rendered","stale","tool"]}},"required":["generation","status","app_url","assets","expires_in_seconds"]},{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"rich_media"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"external_actor_id":{"type":"string"},"app_url":{"type":"string"},"preview_url":{"type":"string"},"credits_used":{"type":"number"},"sizes":{"type":"array","items":{"type":"object","properties":{"size":{"type":"string"},"width":{"type":"number"},"height":{"type":"number"}},"required":["size","width","height"]}},"html":{"type":["string","null"]},"html_available":{"type":"boolean"},"bundle_url":{"type":"string"},"expires_in_seconds":{"type":"number"}},"required":["generation","status","app_url","sizes","html","html_available"]},{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","const":"video"},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"external_actor_id":{"type":"string"},"app_url":{"type":"string"},"preview_url":{"type":"string"},"credits_used":{"type":"number"},"video_url":{"type":"string"},"duration_seconds":{"type":"number"},"aspect_ratio":{"type":"string"},"thumbnail_url":{"type":"string"},"render":{"type":"object","properties":{"required":{"type":"boolean","const":true},"rendered":{"type":"boolean"},"supported":{"type":"boolean"},"tool":{"type":"string","const":"render_generation"},"reason":{"type":"string"}},"required":["required","rendered","supported"]}},"required":["generation","status","app_url","aspect_ratio","render"]}]},"RenderGenerationData":{"$schema":"https://json-schema.org/draft/2020-12/schema","anyOf":[{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"rendered":{"type":"number"},"reused":{"type":"number"},"video_url":{"type":"string"},"resolution":{"type":"string","enum":["1080p","4k"]},"width":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"height":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"app_url":{"type":"string"},"message":{"type":"string"}},"required":["generation","status","rendered","reused","app_url","message"]},{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","const":"rendering"},"rendered":{"type":"number"},"reused":{"type":"number"},"task_queued":{"type":"boolean","const":true},"poll_after_seconds":{"type":"number"},"resolution":{"type":"string","enum":["1080p","4k"]},"width":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"height":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"app_url":{"type":"string"},"message":{"type":"string"}},"required":["generation","status","rendered","reused","task_queued","poll_after_seconds","app_url","message"]},{"type":"object","properties":{"generation":{"type":"object","properties":{"type":{"type":"string","enum":["static_ad","carousel","rich_media","video"]},"id":{"type":"string","minLength":1}},"required":["type","id"]},"status":{"type":"string","enum":["queued","scraping","generating","rendering","completed","rendered","partial","failed"]},"rendered":{"type":"number"},"reused":{"type":"number"},"no_render_needed":{"type":"boolean"},"app_url":{"type":"string"},"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"kind":{"type":"string","const":"image"},"url":{"type":"string"},"width":{"type":"number"},"height":{"type":"number"},"aspect_ratio":{"type":"string"},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["id","kind","url","metadata"]}},"expires_in_seconds":{"type":"number"}},"required":["generation","status","rendered","reused","no_render_needed","app_url"]}]},"CreateUploadData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"asset_url":{"type":"string"},"asset_id":{"type":"string"},"deduplicated":{"type":"boolean"},"upload_url":{"type":"string"},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"expires_in_seconds":{"type":"number"}},"required":["asset_url","deduplicated"]},"ErrorEnvelope":{"type":"object","required":["ok","error"],"properties":{"ok":{"const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"enum":["UNAUTHORIZED","INVALID_SCOPE","ENTITLEMENT_REQUIRED","RATE_LIMITED","VALIDATION_ERROR","BRAND_NOT_FOUND","TEAM_NOT_FOUND","GENERATION_NOT_FOUND","NOT_FOUND","SUBSCRIPTION_REQUIRED","ENTITLEMENT_UNRESOLVED","INSUFFICIENT_CREDITS","GENERATION_NOT_READY","GENERATION_TERMINAL","REQUEST_IN_FLIGHT","GENERATION_FAILED","INTERNAL_ERROR"]},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}}},"WebhookPayload":{"allOf":[{"$ref":"#/components/schemas/GenerationStatusData"},{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"event":{"type":"string","enum":["generation.completed","generation.rendered","generation.partial","generation.failed","generation.render_failed"]},"delivery_id":{"type":"string","minLength":1},"occurred_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$","description":"When the lifecycle event was raised. Stable across delivery retries; use it to order delayed callbacks.","example":"2026-07-24T18:42:17.123Z"},"result":{"$ref":"#/components/schemas/GenerationResultData"}},"required":["event","delivery_id","occurred_at"]}]}},"responses":{"UNAUTHORIZED":{"description":"Authentication is missing or invalid.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"UNAUTHORIZED"}}}}}]}}}},"INVALID_SCOPE":{"description":"The credential lacks the required scope.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INVALID_SCOPE"}}}}}]}}}},"ENTITLEMENT_REQUIRED":{"description":"The account is not on a plan that includes the requested feature (REST API access, or the videoGeneration entitlement for video creation). details.required_entitlement names the missing entitlement.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_REQUIRED"}}}}}]}}}},"RATE_LIMITED":{"description":"The per-key request limit was exceeded.","headers":{"Retry-After":{"description":"Seconds until another request should be attempted.","schema":{"type":"integer","minimum":0}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"RATE_LIMITED"}}}}}]}}}},"VALIDATION_ERROR":{"description":"The request does not match the endpoint schema. Undocumented query parameters, a documented query parameter repeated, and unknown body fields are all rejected, not ignored; details.issues[].path names each offending field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"VALIDATION_ERROR"}}}}}]}}}},"BRAND_NOT_FOUND":{"description":"The brand does not exist in the authenticated account.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"BRAND_NOT_FOUND"}}}}}]}}}},"TEAM_NOT_FOUND":{"description":"The team does not exist or you are not a member of it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"TEAM_NOT_FOUND"}}}}}]}}}},"GENERATION_NOT_FOUND":{"description":"The generation does not exist in the authenticated account.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_FOUND"}}}}}]}}}},"NOT_FOUND":{"description":"A referenced resource (for example the source image of an adaptation) does not exist in the authenticated account.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"NOT_FOUND"}}}}}]}}}},"SUBSCRIPTION_REQUIRED":{"description":"The account has no active subscription, so it cannot start a new generation. details.billing_url points at the team billing page.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"SUBSCRIPTION_REQUIRED"}}}}}]}}}},"ENTITLEMENT_UNRESOLVED":{"description":"The account's plan could not be confirmed, or an internal ceiling no plan lifts was reached: its subscription names a product unknown to the catalog or more than one product (details.reason = unknown_product | ambiguous_subscription), or the team's internal brand limit was reached (details.reason = brand_playground_limit). Not retryable (details.retryable is false) — contact support.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"ENTITLEMENT_UNRESOLVED"}}}}}]}}}},"INSUFFICIENT_CREDITS":{"description":"The account does not have enough credits for this generation. details.required_credits is the estimated cost and details.balance the live balance.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INSUFFICIENT_CREDITS"}}}}}]}}}},"GENERATION_NOT_READY":{"description":"The generation is not ready for this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_NOT_READY"}}}}}]}}}},"GENERATION_TERMINAL":{"description":"The generation already reached a terminal status, so it can no longer be cancelled. details.status carries the terminal status.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_TERMINAL"}}}}}]}}}},"REQUEST_IN_FLIGHT":{"description":"A request with this idempotency key is currently executing.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"REQUEST_IN_FLIGHT"}}}}}]}}}},"GENERATION_FAILED":{"description":"The generation reached a terminal failed state.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"GENERATION_FAILED"}}}}}]}}}},"INTERNAL_ERROR":{"description":"An internal error occurred. Use the correlation id if present.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"properties":{"error":{"properties":{"code":{"const":"INTERNAL_ERROR"}}}}}]}}}}}},"x-creative-mcp-tool-count":16}