{"openapi":"3.1.0","info":{"title":"Qulture Studio API","version":"1.0.0","summary":"Make AI videos and images from your own apps.","description":"Create short AI videos (text-to-video, photo-to-video, templates, camera moves) and images programmatically. Authenticate with an API key from the Console at https://open.qulture.io/console. API keys are pay as you go: each generation is paid from your USD API balance (per second of video, per image; see `GET /api/v1/developer/pricing`), separate from Studio plans. Connector (MCP) keys use Studio plan credits instead. Every success uses the `{ success, message, data }` envelope; errors carry a stable `error` code.","contact":{"name":"Qulture","url":"https://open.qulture.io"}},"externalDocs":{"description":"Developer docs","url":"https://open.qulture.io/docs"},"servers":[{"url":"https://qulture-webapi-staging-381114038302.us-central1.run.app","description":"Qulture API"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Account","description":"Who the key belongs to and its API balance."},{"name":"Billing","description":"Prices and your API balance. `GET /developer/pricing` is public; the other billing endpoints are used by the Console and need the signed-in dashboard session — they can't be called with an API key."},{"name":"Catalog","description":"Templates, camera-move presets, voices and pricing."},{"name":"Uploads","description":"Upload photos (and short audio/video) to use as inputs."},{"name":"Videos","description":"Create and follow video jobs."},{"name":"Images","description":"Create images and turn them into videos."}],"paths":{"/api/v1/developer/me":{"get":{"tags":["Account"],"operationId":"getDeveloperMe","summary":"Check your API key","description":"Returns the key's owner, which key was used, who pays (`payer`) and the API balance (`billing.balance_usd`). The quickest way to test a key. `credits` and `plan` are the Studio plan (used by connector keys).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeveloperMe"}}},"example":{"success":true,"message":"OK","data":{"user":{"id":"172a9b77-06a8-491e-967c-8c245368de28","username":"studiotest","name":"Studio Test"},"credits":6,"plan":"free","key":{"id":"1a976eb1-46bf-44a6-b49a-6254abb5b4ad","name":"My app","prefix":"qk_live_ULnO"},"billing":{"balance_usd":24.5,"currency":"usd"},"payer":"api_balance"}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/developer/pricing":{"get":{"tags":["Billing"],"operationId":"getDeveloperPricing","summary":"API prices","description":"The pay-as-you-go price list in USD, top-up limits and the default concurrency limit. Public: no key needed.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeveloperPricing"}}},"example":{"success":true,"message":"OK","data":{"currency":"usd","prices":{"video_draft_per_second":0.1,"video_standard_per_second":0.18,"video_pro_per_second":0.26,"audio_per_second":0.02,"image_flash":0.06,"image_pro":0.12,"recap":0.3,"story_assembly":0.05,"edit_render_per_minute":0.05,"enhance_per_minute":0.05,"character":0.1},"min_topup_usd":5,"max_topup_usd":5000,"topup_presets_usd":[10,25,100],"default_concurrency":20}}}}}}}},"/api/v1/developer/billing":{"get":{"tags":["Billing"],"operationId":"getDeveloperBilling","summary":"Balance and billing settings","description":"**Dashboard session only; not callable with an API key** (an API key gets 403 `developer_scope_denied`). API balance, status, running generations vs. the concurrency limit, auto-recharge, lifetime totals, 30 days of spend and the latest 20 ledger entries.","security":[{"Session":[]}],"x-session-only":true,"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeveloperBilling"}}},"example":{"success":true,"message":"OK","data":{"balance_usd":24.5,"currency":"usd","status":"active","concurrency_limit":20,"running":1,"auto_recharge":{"enabled":true,"threshold_usd":10,"amount_usd":25,"has_payment_method":true,"card":{"brand":"visa","last4":"4242"},"last_error":null},"low_balance_usd":5,"owed_usd":0,"frozen_usd":0,"lifetime":{"topups_usd":25,"spend_usd":0.5,"refunds_usd":0},"spend_30d":[{"date":"2026-10-29","spend_usd":0.5,"topups_usd":25}],"ledger":[{"id":"0b8a3c1e-5d2f-4b61-9a77-3e4c2f1d9a01","kind":"charge","amount_usd":-0.5,"balance_after_usd":24.5,"reference":"job:3f0c…:charge","job_id":"8d67563e-bbaf-4357-9380-04c2fe602203","image_id":null,"key_id":"1a976eb1-46bf-44a6-b49a-6254abb5b4ad","key_prefix":"qk_live_ULnO","description":"Video · Draft · 5 s","created_at":"2026-10-29T00:20:00Z"}],"billing_enabled":true,"portal_available":true,"prices":{"video_draft_per_second":0.1,"video_standard_per_second":0.18,"video_pro_per_second":0.26,"audio_per_second":0.02,"image_flash":0.06,"image_pro":0.12,"recap":0.3,"story_assembly":0.05,"edit_render_per_minute":0.05,"enhance_per_minute":0.05,"character":0.1}}}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"}}}},"/api/v1/developer/billing/ledger":{"get":{"tags":["Billing"],"operationId":"listDeveloperLedger","summary":"Billing history","description":"**Dashboard session only; not callable with an API key** (an API key gets 403 `developer_scope_denied`). Every charge, top-up and adjustment on the API balance, newest first.","security":[{"Session":[]}],"x-session-only":true,"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"kind","in":"query","schema":{"$ref":"#/components/schemas/LedgerKind"},"description":"Only entries of this kind."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/LedgerEntry"}},"total":{"type":"integer"}}}}},"example":{"success":true,"message":"OK","data":{"items":[{"id":"0b8a3c1e-5d2f-4b61-9a77-3e4c2f1d9a01","kind":"charge","amount_usd":-0.5,"balance_after_usd":24.5,"reference":"job:3f0c…:charge","job_id":"8d67563e-bbaf-4357-9380-04c2fe602203","image_id":null,"key_id":"1a976eb1-46bf-44a6-b49a-6254abb5b4ad","key_prefix":"qk_live_ULnO","description":"Video · Draft · 5 s","created_at":"2026-10-29T00:20:00Z"}],"total":1}}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"}}}},"/api/v1/developer/billing/checkout":{"post":{"tags":["Billing"],"operationId":"createDeveloperCheckout","summary":"Add funds (Stripe Checkout)","description":"**Dashboard session only; not callable with an API key** (an API key gets 403 `developer_scope_denied`). Starts a Stripe Checkout payment for `amount_usd` (between `min_topup_usd` and `max_topup_usd`). Redirect the user to `url`; they come back to the Console with `?topup=success` or `?topup=cancelled`. The card is saved for auto-recharge. Funds are added when Stripe confirms the payment.","security":[{"Session":[]}],"x-session-only":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_usd"],"properties":{"amount_usd":{"type":"number","minimum":5,"maximum":5000},"return_url":{"type":"string","format":"uri"}}},"example":{"amount_usd":25}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"session_id":{"type":"string"}}}}},"example":{"success":true,"message":"OK","data":{"url":"https://checkout.stripe.com/c/pay/cs_…","session_id":"cs_…"}}}}},"400":{"description":"Amount out of range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Top-ups are between $5 and $5,000.","error":"developer_invalid_amount"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"}}}},"/api/v1/developer/billing/auto-recharge":{"put":{"tags":["Billing"],"operationId":"updateDeveloperAutoRecharge","summary":"Auto-recharge settings","description":"**Dashboard session only; not callable with an API key** (an API key gets 403 `developer_scope_denied`). When the balance falls below `threshold_usd`, charge the saved card `amount_usd`. Enabling needs a saved card (make one top-up first). Returns the billing object.","security":[{"Session":[]}],"x-session-only":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["enabled","threshold_usd","amount_usd"],"properties":{"enabled":{"type":"boolean"},"threshold_usd":{"type":"number","minimum":0},"amount_usd":{"type":"number","minimum":5,"maximum":5000}}},"example":{"enabled":true,"threshold_usd":10,"amount_usd":25}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DeveloperBilling"}}},"example":{"success":true,"message":"OK","data":{"balance_usd":24.5,"currency":"usd","status":"active","concurrency_limit":20,"running":1,"auto_recharge":{"enabled":true,"threshold_usd":10,"amount_usd":25,"has_payment_method":true,"card":{"brand":"visa","last4":"4242"},"last_error":null},"low_balance_usd":5,"owed_usd":0,"frozen_usd":0,"lifetime":{"topups_usd":25,"spend_usd":0.5,"refunds_usd":0},"spend_30d":[{"date":"2026-10-29","spend_usd":0.5,"topups_usd":25}],"ledger":[{"id":"0b8a3c1e-5d2f-4b61-9a77-3e4c2f1d9a01","kind":"charge","amount_usd":-0.5,"balance_after_usd":24.5,"reference":"job:3f0c…:charge","job_id":"8d67563e-bbaf-4357-9380-04c2fe602203","image_id":null,"key_id":"1a976eb1-46bf-44a6-b49a-6254abb5b4ad","key_prefix":"qk_live_ULnO","description":"Video · Draft · 5 s","created_at":"2026-10-29T00:20:00Z"}],"billing_enabled":true,"portal_available":true,"prices":{"video_draft_per_second":0.1,"video_standard_per_second":0.18,"video_pro_per_second":0.26,"audio_per_second":0.02,"image_flash":0.06,"image_pro":0.12,"recap":0.3,"story_assembly":0.05,"edit_render_per_minute":0.05,"enhance_per_minute":0.05,"character":0.1}}}}}},"400":{"description":"Amount out of range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Top-ups are between $5 and $5,000.","error":"developer_invalid_amount"}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"409":{"description":"No saved card","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Add a card with a top-up first.","error":"developer_payment_method_required"}}}}}}},"/api/v1/developer/billing/portal":{"post":{"tags":["Billing"],"operationId":"createDeveloperPortal","summary":"Payment methods and receipts","description":"**Dashboard session only; not callable with an API key** (an API key gets 403 `developer_scope_denied`). Opens the Stripe customer portal (saved cards, receipts). Redirect the user to `url`.","security":[{"Session":[]}],"x-session-only":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"return_url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}},"example":{"success":true,"message":"OK","data":{"url":"https://billing.stripe.com/p/session/…"}}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"}}}},"/api/v1/studio/me":{"get":{"tags":["Account"],"operationId":"getStudioMe","summary":"Studio plan, credits and limits","description":"Studio plan credits, next refill, plan limits and the credit price list. These apply to connector (MCP) keys; API keys pay from the API balance (see `GET /developer/me`).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/StudioMe"}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/catalog":{"get":{"tags":["Catalog"],"operationId":"getCatalog","summary":"Public catalog and pricing","description":"Templates, presets, Studio plans and the Studio credit price list. Public: no key needed. API prices in USD are at `GET /developer/pricing`.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Catalog"}}}}}}}}},"/api/v1/studio/templates":{"get":{"tags":["Catalog"],"operationId":"listTemplates","summary":"List templates","description":"Ready-made video ideas. Each lists the `inputs` it needs (photo, text…), its defaults and credit cost.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TemplateList"}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/presets":{"get":{"tags":["Catalog"],"operationId":"listPresets","summary":"List camera moves and effects","description":"Camera-move presets (dolly in, orbit, whip pan…), effects and styles. Pass a preset `id` as `preset_id` when you create a job.","parameters":[{"name":"category","in":"query","schema":{"type":"string"},"description":"Filter by category id, e.g. `classic`, `dynamic`, `effects`."},{"name":"mode","in":"query","schema":{"$ref":"#/components/schemas/Mode"},"description":"Only presets that work with this mode."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PresetList"}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/voices":{"get":{"tags":["Catalog"],"operationId":"listVoices","summary":"List voices","description":"Voices for `talking_photo` jobs (`voice` + `speech_text`).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"voices":{"type":"array","items":{"$ref":"#/components/schemas/Voice"}}}}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/uploads":{"post":{"tags":["Uploads"],"operationId":"upload","summary":"Upload a photo","description":"Multipart upload. Images: JPEG, PNG, WebP or HEIC up to 10 MB (normalised to JPEG). `kind=audio` (≤ 15 s) and `kind=video` (≤ 15 s, ≤ 50 MB) are accepted too. Use the returned `key` in `image_keys` (or a template's image input). If `has_face` is true, send `consent: true` with the job.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"kind":{"type":"string","enum":["image","audio","video"],"default":"image"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Upload"}}},"example":{"success":true,"message":"OK","data":{"key":"studio/inputs/172a9b77-…/a4095776-….jpg","url":"https://media.joinqulture.com/studio/inputs/…","width":1080,"height":1350,"has_face":true,"kind":"image"}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"413":{"$ref":"#/components/responses/E413"},"415":{"$ref":"#/components/responses/E415"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/jobs":{"post":{"tags":["Videos"],"operationId":"createJob","summary":"Create a video","description":"Starts a video job and charges its price to your API balance (duration × the tier's price per second; `cost_usd` on the job). The response is the job (usually `queued`); follow it with `GET /jobs/{id}` or pass `webhook_url` to be called when it finishes. You're charged when a generation is submitted and finishes processing, whatever its outcome. Requests rejected before processing (invalid input, insufficient balance, limits) are never charged: not enough balance → 402 `insufficient_balance` (no job is created).","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJob"},"examples":{"text_to_video":{"summary":"Text to video (Draft, 5 s)","value":{"mode":"text_to_video","prompt":"A neon-lit street market at night, slow camera push in","tier":"draft","duration":5,"aspect":"9:16"}},"photo_to_video":{"summary":"Photo to video with a camera move","value":{"mode":"photo_to_video","image_keys":["studio/inputs/…/photo.jpg"],"prompt":"She turns and smiles","preset_id":"dolly-in","tier":"draft","duration":5,"consent":true}},"template":{"summary":"Template","value":{"mode":"template","template_id":"party-intro","inputs":{"photo":"studio/inputs/…/photo.jpg"},"tier":"draft","duration":5,"consent":true}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}},"example":{"success":true,"message":"OK","data":{"id":"8d67563e-bbaf-4357-9380-04c2fe602203","template_id":null,"template_name":null,"mode":"text_to_video","status":"queued","progress":0,"stage_label":"In the queue","queue_position":1,"duration_s":5,"quality":"standard","sound":false,"credits_charged":0,"billing":"api_balance","cost_usd":0.5,"charged_usd":0.5,"video_url":null,"download_url":null,"thumbnail_url":null,"watermarked":false,"caption":null,"hashtags":[],"fail_reason":null,"block_category":null,"posted_media_id":null,"prompt":"A neon-lit street market at night, slow camera push in","inputs":{},"created_at":"2026-10-29T00:20:00Z","finished_at":null,"expires_at":null,"kind":"video","tier":"draft","aspect":"9:16","preset_id":null,"character_id":null,"parent_job_id":null,"story_id":null,"shot_id":null,"avatar_user_ids":[],"needs_approval":false,"post_status":""}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429Gen"},"503":{"$ref":"#/components/responses/E503"}}},"get":{"tags":["Videos"],"operationId":"listJobs","summary":"List your videos","parameters":[{"name":"cursor","in":"query","schema":{"type":"string"},"description":"`next_cursor` from the previous page."},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"next_cursor":{"type":["string","null"]}}}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/jobs/{id}":{"parameters":[{"$ref":"#/components/parameters/JobId"}],"get":{"tags":["Videos"],"operationId":"getJob","summary":"Get a video","description":"Status, progress and — once `ready` — signed `video_url` / `download_url` (valid for 1 hour; fetch the job again for fresh links). Poll every 5 s.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}},"example":{"success":true,"message":"OK","data":{"id":"8d67563e-bbaf-4357-9380-04c2fe602203","template_id":null,"template_name":null,"mode":"text_to_video","status":"ready","progress":1,"stage_label":"Ready","queue_position":null,"duration_s":5,"quality":"standard","sound":false,"credits_charged":0,"billing":"api_balance","cost_usd":0.5,"charged_usd":0.5,"video_url":"https://media.joinqulture.com/studio/out/…/marked.mp4?X-Amz-Signature=…","download_url":"https://media.joinqulture.com/studio/out/…/marked.mp4?…&response-content-disposition=attachment","thumbnail_url":"https://media.joinqulture.com/studio/out/…/thumb.jpg?…","watermarked":false,"caption":"Night market glow ✨","hashtags":["#MadeWithQulture"],"fail_reason":null,"block_category":null,"posted_media_id":null,"prompt":"A neon-lit street market at night, slow camera push in","inputs":{},"created_at":"2026-10-29T00:20:00Z","finished_at":"2026-10-29T00:21:10Z","expires_at":null,"kind":"video","tier":"draft","aspect":"9:16","preset_id":null,"character_id":null,"parent_job_id":null,"story_id":null,"shot_id":null,"avatar_user_ids":[],"needs_approval":false,"post_status":""}}}}},"401":{"$ref":"#/components/responses/E401"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}},"delete":{"tags":["Videos"],"operationId":"deleteJob","summary":"Delete a video","description":"Deletes the creation and its files. Jobs still in progress return 409 `studio_job_not_ready`.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"null"}}},"example":{"success":true,"message":"OK","data":null}}}},"401":{"$ref":"#/components/responses/E401"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/jobs/{id}/regenerate":{"parameters":[{"$ref":"#/components/parameters/JobId"}],"post":{"tags":["Videos"],"operationId":"regenerateJob","summary":"Regenerate","description":"Same inputs, new seed. A new job, charged again. Accepts an optional JSON body with `webhook_url`.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"webhook_url":{"$ref":"#/components/schemas/WebhookURL"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}}}}},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429Gen"}}}},"/api/v1/studio/jobs/{id}/extend":{"parameters":[{"$ref":"#/components/parameters/JobId"}],"post":{"tags":["Videos"],"operationId":"extendJob","summary":"Extend a video","description":"Continues a ready video with a new prompt. Returns a new job with `parent_job_id` set.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt","duration"],"properties":{"prompt":{"type":"string","maxLength":500},"duration":{"type":"integer","enum":[5,10]},"tier":{"$ref":"#/components/schemas/Tier"},"webhook_url":{"$ref":"#/components/schemas/WebhookURL"}}},"example":{"prompt":"The camera rises above the market","duration":5,"tier":"draft"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429Gen"}}}},"/api/v1/studio/jobs/{id}/upgrade":{"parameters":[{"$ref":"#/components/parameters/JobId"}],"post":{"tags":["Videos"],"operationId":"upgradeJob","summary":"Upgrade a Draft","description":"Re-renders a Draft with the same shot plan and seed at a higher tier. A new job, charged at the new tier's price.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tier"],"properties":{"tier":{"type":"string","enum":["standard","pro"]},"webhook_url":{"$ref":"#/components/schemas/WebhookURL"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}}}}},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429Gen"}}}},"/api/v1/studio/images":{"post":{"tags":["Images"],"operationId":"createImage","summary":"Create an image","description":"Generates an image from a prompt (optionally guided by uploaded `image_keys`). Follow it with `GET /images/{id}` or `webhook_url`.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImage"},"example":{"prompt":"Poster of a rooftop party at golden hour","quality":"flash","aspect":"9:16"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Image"}}},"example":{"success":true,"message":"OK","data":{"id":"4b1f0f1e-9a52-4c43-8d0f-3f7d8f8f2a10","prompt":"Poster of a rooftop party at golden hour","status":"queued","image_url":null,"width":null,"height":null,"billing":"api_balance","cost_usd":0.06,"charged_usd":0.06,"created_at":"2026-10-29T00:20:00Z"}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429Gen"}}},"get":{"tags":["Images"],"operationId":"listImages","summary":"List your images","parameters":[{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"type":"object","properties":{"images":{"type":"array","items":{"$ref":"#/components/schemas/Image"}},"next_cursor":{"type":["string","null"]}}}}}}}},"401":{"$ref":"#/components/responses/E401"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/images/{id}":{"parameters":[{"$ref":"#/components/parameters/ImageId"}],"get":{"tags":["Images"],"operationId":"getImage","summary":"Get an image","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Image"}}},"example":{"success":true,"message":"OK","data":{"id":"4b1f0f1e-9a52-4c43-8d0f-3f7d8f8f2a10","prompt":"Poster of a rooftop party at golden hour","status":"ready","image_url":"https://media.joinqulture.com/…/image.jpg?…","width":1080,"height":1920,"billing":"api_balance","cost_usd":0.06,"charged_usd":0.06,"created_at":"2026-10-29T00:20:00Z"}}}}},"401":{"$ref":"#/components/responses/E401"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/v1/studio/images/{id}/animate":{"parameters":[{"$ref":"#/components/parameters/ImageId"}],"post":{"tags":["Images"],"operationId":"animateImage","summary":"Turn an image into a video","description":"Creates a `photo_to_video` job from a ready image. Same body as `POST /jobs` without image inputs.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJob"},"example":{"mode":"photo_to_video","prompt":"Slow orbit around the poster","tier":"draft","duration":5}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["success","message","data"],"properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/Job"}}}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"402":{"$ref":"#/components/responses/E402"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429Gen"}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"qk_live_…","description":"Send `Authorization: Bearer qk_live_…`. Create keys in the Console: https://open.qulture.io/console."},"Session":{"type":"http","scheme":"bearer","description":"The signed-in Qulture dashboard session (used by the Console). Not an API key: API keys get 403 `developer_scope_denied` here."}},"parameters":{"JobId":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Job id"},"ImageId":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Image id"},"IdempotencyKey":{"name":"X-Qulture-Request-ID","in":"header","required":false,"schema":{"type":"string","maxLength":128},"description":"Any unique string. Retrying with the same value never creates (or charges) twice."}},"responses":{"E400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Describe the video you want.","error":"studio_invalid_input"}}}},"E401":{"description":"Missing, invalid or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"This API key is not valid.","error":"developer_key_invalid"}}}},"E402":{"description":"Not enough API balance (`insufficient_balance`; connector keys: `studio_insufficient_credits`). Nothing is created or charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Your API balance is $0.20 and this costs $0.50. Add funds at https://open.qulture.io/console.","error":"insufficient_balance"}}}},"E403":{"description":"Not allowed: scope, Studio disabled, or account suspended (`developer_account_suspended`, e.g. after a refunded or disputed top-up)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"API keys can't use this endpoint.","error":"developer_scope_denied"}}}},"E404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"We couldn't find that.","error":"studio_not_found"}}}},"E409":{"description":"Not ready yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"This video is still being made.","error":"studio_job_not_ready"}}}},"E413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"That file is too large.","error":"studio_file_too_large"}}}},"E415":{"description":"Unsupported file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Use a JPEG, PNG, WebP or HEIC photo.","error":"studio_unsupported_image"}}}},"E422":{"description":"Blocked by safety checks, or consent needed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"We can't make this video.","error":"studio_blocked"}}}},"E429":{"description":"Rate limited: 60 requests/minute per key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Too many requests for this API key (60 per minute).","error":"rate_limited","retry_after":12}}}},"E429Gen":{"description":"Rate limited (`rate_limited`: 60 requests/minute, or 200 generations/hour per API key — 20 for connector keys) or too many generations running at once for the account (`concurrency_limit`, 20)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"You have 20 generations running. Wait for one to finish and try again.","error":"concurrency_limit"}}}},"E503":{"description":"Busy — try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"success":false,"message":"Studio is busy right now.","error":"studio_busy"}}}}},"schemas":{"Error":{"type":"object","required":["success","message"],"properties":{"success":{"type":"boolean","const":false},"message":{"type":"string","description":"Human-readable; safe to show."},"error":{"type":"string","description":"Stable machine code — branch on this."},"retry_after":{"type":"integer","description":"Seconds to wait (429 only)."},"block_category":{"type":"string","description":"Why a request was blocked (422 `studio_blocked`)."},"errors":{"type":"array","description":"Field validation failures (422).","items":{"type":"object","properties":{"key":{"type":"string"},"error":{"type":"string"},"condition":{"type":"string"}}}}}},"Mode":{"type":"string","enum":["text_to_video","photo_to_video","template","transition","reference","character","talking_photo","sound_sync","describe","animate"],"description":"What kind of video to make."},"Tier":{"type":"string","enum":["draft","standard","pro"],"description":"Quality tier, priced per second. Draft is cheapest."},"Aspect":{"type":"string","enum":["9:16","1:1","4:5","16:9"],"default":"9:16"},"WebhookURL":{"type":"string","format":"uri","maxLength":2000,"description":"https URL Qulture POSTs a signed event to when the job finishes. API keys only."},"CreateJob":{"type":"object","required":["mode"],"properties":{"mode":{"$ref":"#/components/schemas/Mode"},"prompt":{"type":"string","maxLength":2000,"description":"What should happen. Required for text_to_video."},"tier":{"$ref":"#/components/schemas/Tier"},"duration":{"type":"integer","enum":[5,10],"default":5,"description":"Seconds. Billed per second."},"aspect":{"$ref":"#/components/schemas/Aspect"},"preset_id":{"type":"string","description":"Camera move / effect id from `GET /presets`."},"template_id":{"type":"string","description":"Template id from `GET /templates` (mode `template`)."},"inputs":{"type":"object","additionalProperties":true,"description":"Template inputs by key: upload keys for photos, strings for text."},"image_keys":{"type":"array","items":{"type":"string"},"maxItems":9,"description":"Upload keys (photo_to_video, reference, transition = [first, last])."},"sound":{"type":"boolean","default":false,"description":"Generate sound."},"consent":{"type":"boolean","default":false,"description":"Required when a photo shows a person: you confirm you have their permission."},"character_id":{"type":"string","format":"uuid"},"voice":{"type":"string","description":"Voice id (talking_photo)."},"speech_text":{"type":"string","maxLength":400,"description":"What the photo says (talking_photo)."},"audio_key":{"type":"string","description":"Uploaded audio (sound_sync)."},"webhook_url":{"$ref":"#/components/schemas/WebhookURL"}}},"Job":{"type":"object","description":"A video job. Statuses: queued → planning → rendering → finishing → ready (or failed / blocked).","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["video","image"]},"mode":{"$ref":"#/components/schemas/Mode"},"status":{"type":"string","enum":["queued","planning","rendering","finishing","ready","failed","blocked"]},"progress":{"type":"number","minimum":0,"maximum":1},"stage_label":{"type":"string"},"queue_position":{"type":["integer","null"]},"tier":{"$ref":"#/components/schemas/Tier"},"aspect":{"$ref":"#/components/schemas/Aspect"},"duration_s":{"type":"integer"},"quality":{"type":"string"},"sound":{"type":"boolean"},"credits_charged":{"type":"integer","description":"Studio plan credits charged (connector keys and the apps). 0 when paid from the API balance."},"billing":{"$ref":"#/components/schemas/BillingSource"},"cost_usd":{"type":"number","description":"Price held at submit, in USD. Present when paid from the API balance."},"charged_usd":{"type":"number","description":"Amount charged, in USD. Equals `cost_usd` once the job ends. Manual adjustments by Qulture appear in the ledger as `adjustment`. Present when paid from the API balance."},"prompt":{"type":["string","null"]},"template_id":{"type":["string","null"]},"template_name":{"type":["string","null"]},"preset_id":{"type":["string","null"]},"character_id":{"type":["string","null"]},"parent_job_id":{"type":["string","null"]},"inputs":{"type":"object","additionalProperties":true},"video_url":{"type":["string","null"],"description":"Signed, valid 1 hour."},"download_url":{"type":["string","null"],"description":"Same file as an attachment."},"thumbnail_url":{"type":["string","null"]},"watermarked":{"type":"boolean","description":"True when the file carries a small Qulture mark (free Studio plan). Videos paid from the API balance are clean."},"caption":{"type":["string","null"]},"hashtags":{"type":"array","items":{"type":"string"}},"fail_reason":{"type":["string","null"]},"block_category":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the files are removed, if they expire. Copy files you want to keep."}}},"CreateImage":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":1000},"quality":{"type":"string","enum":["flash","pro"],"default":"flash"},"aspect":{"$ref":"#/components/schemas/Aspect"},"image_keys":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Optional reference photos (upload keys)."},"webhook_url":{"$ref":"#/components/schemas/WebhookURL"}}},"Image":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"prompt":{"type":"string"},"status":{"type":"string","enum":["queued","ready","failed","blocked"]},"image_url":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"billing":{"$ref":"#/components/schemas/BillingSource"},"cost_usd":{"type":"number","description":"Price held at submit, in USD. Present when paid from the API balance."},"charged_usd":{"type":"number","description":"Amount charged, in USD. Equals `cost_usd` once the image ends. Manual adjustments by Qulture appear in the ledger as `adjustment`. Present when paid from the API balance."},"created_at":{"type":"string","format":"date-time"}}},"BillingSource":{"type":"string","enum":["api_balance"],"description":"`api_balance` when the generation is paid from the developer API balance. Absent when paid with Studio plan credits."},"Upload":{"type":"object","properties":{"key":{"type":"string"},"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"has_face":{"type":"boolean"},"kind":{"type":"string","enum":["image","audio","video"]},"duration_s":{"type":"number"}}},"DeveloperMe":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"username":{"type":["string","null"]},"name":{"type":"string"}}},"credits":{"type":"integer","description":"Studio plan credits (spent by connector keys)."},"plan":{"type":"string","description":"Studio plan."},"key":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"}}},"payer":{"type":"string","enum":["api_balance","studio_credits"],"description":"Who pays for generations made with this key: the API balance (dashboard keys) or Studio plan credits (connector keys)."},"billing":{"type":"object","description":"The API balance. Included for dashboard keys and the session.","properties":{"balance_usd":{"type":"number"},"currency":{"type":"string","const":"usd"}}}}},"DeveloperPrices":{"type":"object","description":"USD prices. `video_*_per_second` × duration; `audio_per_second` is added for talking_photo and sound_sync.","properties":{"video_draft_per_second":{"type":"number"},"video_standard_per_second":{"type":"number"},"video_pro_per_second":{"type":"number"},"audio_per_second":{"type":"number"},"image_flash":{"type":"number"},"image_pro":{"type":"number"},"recap":{"type":"number"},"story_assembly":{"type":"number"},"edit_render_per_minute":{"type":"number"},"enhance_per_minute":{"type":"number"},"character":{"type":"number"}},"example":{"video_draft_per_second":0.1,"video_standard_per_second":0.18,"video_pro_per_second":0.26,"audio_per_second":0.02,"image_flash":0.06,"image_pro":0.12,"recap":0.3,"story_assembly":0.05,"edit_render_per_minute":0.05,"enhance_per_minute":0.05,"character":0.1}},"DeveloperPricing":{"type":"object","properties":{"currency":{"type":"string","const":"usd"},"prices":{"$ref":"#/components/schemas/DeveloperPrices"},"min_topup_usd":{"type":"number"},"max_topup_usd":{"type":"number"},"topup_presets_usd":{"type":"array","items":{"type":"number"}},"default_concurrency":{"type":"integer","description":"Generations that may run at once per account."}}},"LedgerKind":{"type":"string","enum":["topup","charge","refund","adjustment","promo","auto_recharge","stripe_refund","dispute","dispute_reversal"],"description":"`refund`: a manual refund by Qulture. `stripe_refund` / `dispute` / `dispute_reversal`: a top-up payment refunded or disputed through Stripe."},"LedgerEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"$ref":"#/components/schemas/LedgerKind"},"amount_usd":{"type":"number","description":"Signed: negative for charges."},"balance_after_usd":{"type":"number"},"reference":{"type":"string"},"job_id":{"type":["string","null"]},"image_id":{"type":["string","null"]},"key_id":{"type":["string","null"]},"key_prefix":{"type":["string","null"]},"description":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}},"DeveloperBilling":{"type":"object","properties":{"balance_usd":{"type":"number"},"currency":{"type":"string"},"status":{"type":"string","enum":["active","suspended"]},"concurrency_limit":{"type":"integer"},"running":{"type":"integer","description":"Generations in progress (they count against the concurrency limit)."},"auto_recharge":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold_usd":{"type":"number"},"amount_usd":{"type":"number"},"has_payment_method":{"type":"boolean"},"card":{"type":["object","null"],"properties":{"brand":{"type":"string"},"last4":{"type":"string"}}},"last_error":{"type":["string","null"]}}},"low_balance_usd":{"type":"number"},"owed_usd":{"type":"number","description":"Owed after a top-up was refunded or disputed (usually 0). API usage is paused until a top-up settles it; top-ups pay it first."},"frozen_usd":{"type":"number","description":"On hold while a payment dispute is reviewed (usually 0)."},"lifetime":{"type":"object","properties":{"topups_usd":{"type":"number"},"spend_usd":{"type":"number"},"refunds_usd":{"type":"number"}}},"spend_30d":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"spend_usd":{"type":"number"},"topups_usd":{"type":"number"}}}},"ledger":{"type":"array","items":{"$ref":"#/components/schemas/LedgerEntry"},"description":"Latest 20 entries."},"billing_enabled":{"type":"boolean"},"portal_available":{"type":"boolean"},"prices":{"$ref":"#/components/schemas/DeveloperPrices"}}},"StudioMe":{"type":"object","properties":{"plan":{"type":"string","enum":["free","plus","pro","studio"]},"credits":{"type":"integer"},"next_refill_at":{"type":["string","null"],"format":"date-time"},"can_remove_watermark":{"type":"boolean"},"max_duration":{"type":"integer"},"daily_jobs_remaining":{"type":"integer"},"studio_enabled":{"type":"boolean"},"limits":{"type":"object","properties":{"max_duration_s":{"type":"integer"},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/Tier"}}}},"pricing":{"$ref":"#/components/schemas/Pricing"}}},"Pricing":{"type":"object","description":"Studio plan credit costs (apps and connector keys). video_* are per 5 seconds. API keys are billed in USD instead (`GET /developer/pricing`).","additionalProperties":{"type":"integer"},"example":{"video_draft":5,"video_standard":9,"video_pro":14,"audio_extra":2,"image_flash":1,"image_pro":2}},"Template":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"preview_url":{"type":["string","null"]},"poster_url":{"type":["string","null"]},"inputs":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["image","images","text","event","circle_plan"]},"required":{"type":"boolean"},"label":{"type":"string"},"max":{"type":"integer"},"face":{"type":"boolean"}}}},"defaults":{"type":"object","properties":{"duration":{"type":"integer"},"quality":{"type":"string"},"sound":{"type":"boolean"}}},"credits":{"type":"object","additionalProperties":{"type":"integer"}},"badge":{"type":["string","null"]}}},"TemplateList":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}},"Preset":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"preview_url":{"type":["string","null"]},"poster_url":{"type":["string","null"]},"recommended_duration":{"type":"integer"},"tags":{"type":"array","items":{"type":"string"}},"modes":{"type":"array","items":{"$ref":"#/components/schemas/Mode"}},"badge":{"type":["string","null"]}}},"PresetList":{"type":"object","properties":{"presets":{"type":"array","items":{"$ref":"#/components/schemas/Preset"}},"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}},"Category":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"Voice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"sample_url":{"type":["string","null"]}}},"Catalog":{"type":"object","properties":{"templates":{"$ref":"#/components/schemas/TemplateList"},"presets":{"$ref":"#/components/schemas/PresetList"},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"title":{"type":"string"},"price_display":{"type":"string"},"credits":{"type":"integer"}}}},"pricing":{"$ref":"#/components/schemas/Pricing"}}},"WebhookEvent":{"type":"object","description":"Body of a webhook. Verify `Qulture-Signature` before trusting it.","properties":{"id":{"type":"string","description":"Delivery id (also in `Qulture-Delivery`). Use it to de-duplicate."},"event":{"type":"string","enum":["video.succeeded","video.failed","image.succeeded","image.failed","ping"]},"created_at":{"type":"string","format":"date-time"},"data":{"oneOf":[{"$ref":"#/components/schemas/Job"},{"$ref":"#/components/schemas/Image"}]}}}}}}