{"openapi":"3.1.0","info":{"title":"GhostShorts API","version":"1.0.0","description":"Create GhostShorts videos from scripts, automation tools and AI agents. API keys use your plan's credits; there is no separate API tier.\n\nVideos are asynchronous: POST /v1/videos answers 202 with a video id; poll GET /v1/videos/{id} (every ~20s) or pass webhookUrl.\n\nWebhooks: POST to your https webhookUrl with JSON { id, type: \"video.completed\" | \"video.failed\", createdAt, data: <Video> } and headers X-GhostShorts-Event and X-GhostShorts-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw request body>\">. The HMAC key is webhooks.signingSecret from GET /v1/me with the whsec_ prefix removed. Reject a delivery whose t is more than 5 minutes old. Deliveries time out after 10s and are retried 3 times (after 10s, 60s and 300s); redirects are not followed; answer 2xx to acknowledge.\n\nPlain-text guides for agents (no key): https://api.ghostshorts.com/v1/llms.txt (the whole API) and https://api.ghostshorts.com/v1/templates/{id}.txt (one template: inputs, a full example, credits, errors)."},"servers":[{"url":"https://api.ghostshorts.com"}],"security":[{"bearerAuth":[]}],"x-ghostshorts-templates":["article_video","quiz_video","reddit_story","fake_text_message"],"paths":{"/v1/me":{"get":{"operationId":"getMe","summary":"Account, plan, entitlement, usage and limits for this API key","responses":{"200":{"description":"The account behind the key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/templates":{"get":{"operationId":"listTemplates","summary":"Templates, each with its input JSON Schema and defaults. No API key needed.","security":[],"responses":{"200":{"description":"Templates in a stable order","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/options":{"get":{"operationId":"getOptions","summary":"Voices, background videos, music and caption presets to choose from. No API key needed.","security":[],"responses":{"200":{"description":"Choosable options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Options"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/estimate":{"post":{"operationId":"estimateCost","summary":"Estimate the credits a video would cost. Free; charges nothing.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replays the stored response for 24h when the same key and body are sent again."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateRequest"}}}},"responses":{"200":{"description":"The estimate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Estimate"}}}},"400":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency key in progress, or the video can no longer be cancelled","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Idempotency key reused with a different body","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/scripts":{"post":{"operationId":"generateScript","summary":"FREE: turn an article URL or text into a script, or a topic into a quiz pack. Returns a projectId to pass to POST /v1/videos.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replays the stored response for 24h when the same key and body are sent again. Send one on every create: agents retry."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptRequest"}}}},"responses":{"200":{"description":"The script (article_video) or pack (quiz_video), saved on a new draft project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Script"}}}},"400":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency key in progress, or the video can no longer be cancelled","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Idempotency key reused with a different body","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"Upstream failure (script generation)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/previews":{"post":{"operationId":"previewNarration","summary":"FREE (0 credits): a ~10 second narration teaser of a reddit_story (the title plus the first ~170 characters). Returns a projectId to pass to POST /v1/videos. Idempotent per project.","description":"No plan needed. A second call with the same projectId returns the stored teaser. The teaser is never used as the video narration: POST /v1/videos always narrates the full story.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replays the stored response for 24h when the same key and body are sent again. Send one on every create: agents retry."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}}},"responses":{"200":{"description":"The teaser audio and its word captions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preview"}}}},"400":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency key in progress, or the video can no longer be cancelled","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Idempotency key reused with a different body","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"Upstream failure (script generation)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/videos":{"post":{"operationId":"createVideo","summary":"Create a video. Answers 202 at once; the video is made asynchronously.","description":"Both money gates are checked BEFORE anything runs, so a refusal (402) costs nothing. Up to 3 videos per account can be in progress at once (429 CONCURRENCY_LIMIT, Retry-After: 30). A replay with the same Idempotency-Key and body answers with the video as it is now (current status and downloadUrl), marked Idempotent-Replayed: true; nothing is created or charged again.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replays the stored response for 24h when the same key and body are sent again. Send one on every create: agents retry."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVideoRequest"}}}},"responses":{"202":{"description":"Accepted and queued","headers":{"Location":{"schema":{"type":"string"},"description":"/v1/videos/{id}"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Video"}}}},"400":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"Plan or balance does not cover this","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency key in progress, or the video can no longer be cancelled","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Idempotency key reused with a different body","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listVideos","summary":"Videos created through the API, newest first","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"nextCursor from the previous page."}],"responses":{"200":{"description":"One page","content":{"application/json":{"schema":{"type":"object","required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Video"}},"nextCursor":{"type":["string","null"]}}}}}},"400":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/videos/{id}":{"get":{"operationId":"getVideo","summary":"Status, progress, download link and error of one video","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The video","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Video"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/videos/{id}/cancel":{"post":{"operationId":"cancelVideo","summary":"Cancel a video that has not started yet (status queued). Best effort.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Replays the stored response for 24h when the same key and body are sent again. Send one on every create: agents retry."}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Video"}}}},"401":{"description":"Missing, invalid or revoked API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency key in progress, or the video can no longer be cancelled","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/d/{token}":{"get":{"operationId":"downloadVideo","summary":"Public download link (no auth). 302 to a fresh signed URL of the MP4. Valid 7 days after completion.","security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to the MP4","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"410":{"description":"Download link expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/llms.txt":{"get":{"operationId":"getAgentGuide","summary":"Plain-text guide to the whole API for AI agents (auth, flow, limits, webhooks, idempotency, templates, errors). No API key needed.","security":[],"responses":{"200":{"description":"The guide","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/templates/{id}.txt":{"get":{"operationId":"getTemplateGuide","summary":"Plain-text guide to one template for AI agents: inputs, defaults, a full example, credits, notes and errors. No API key needed.","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","enum":["article_video","quiz_video","reddit_story","fake_text_message"]}}],"responses":{"200":{"description":"The guide","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This document. No API key needed.","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Rate limiter or queue unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key from https://ghostshorts.com/app/settings/api-keys (gs_live_...)."}},"schemas":{"article_video_inputs":{"type":"object","properties":{"source":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"url"},"url":{"type":"string","format":"uri","description":"Public article, blog or newsletter URL."}},"required":["kind","url"]},{"type":"object","properties":{"kind":{"type":"string","const":"text"},"text":{"type":"string","minLength":1,"maxLength":60000,"description":"Pasted article text. The script step needs 90 to 4000 words."}},"required":["kind","text"]}],"description":"Where the article comes from: a URL we fetch, or the text itself."},"length":{"default":"standard","description":"short ~30s, standard ~60s, deep ~90s.","type":"string","enum":["short","standard","deep"]},"tone":{"default":"explainer","type":"string","enum":["explainer","punchy","authority"]},"voiceId":{"default":"onwK4e9ZLuTAKqWW03F9","description":"Narrator voice. See GET /v1/options voices tagged article_video.","type":"string","enum":["onwK4e9ZLuTAKqWW03F9","JBFqnCBsd6RMkjVDRZzb","EXAVITQu4vr4xnSDxMaL","pNInz6obpgDQGcFmaJgB","LcfcDJNUP1GQjkzn1xUU","TxGEqnHWrfWFTfGW9XjX"]},"captionPresetId":{"description":"Caption style. See GET /v1/options captionPresets.","type":"string","enum":["hormozi","beast","ali","iman","devin","matt","kendrick","laura","doug","carlos","nick","fire","neon","william","leon","luke","classicWhite"]},"silent":{"default":false,"description":"Render with no narration. Refused unless explicitly true.","type":"boolean"}},"required":["source"],"additionalProperties":false,"description":"Plain-text guide for agents: https://api.ghostshorts.com/v1/templates/article_video.txt"},"quiz_video_inputs":{"type":"object","properties":{"topic":{"type":"string","minLength":2,"maxLength":200,"description":"What the quiz is about, e.g. \"the Roman Empire\"."},"difficulty":{"default":"medium","type":"string","enum":["easy","medium","hard"]},"questionCount":{"default":5,"anyOf":[{"type":"number","const":5},{"type":"number","const":7},{"type":"number","const":10}]},"categoryId":{"description":"Optional steer for the generator.","type":"string","enum":["general-knowledge","geography","history","science","movies-tv","music","sports","food-drink","video-games","logos-brands"]},"videoTitle":{"description":"Title on the intro card.","type":"string","minLength":1,"maxLength":48},"voiceId":{"default":"LcfcDJNUP1GQjkzn1xUU","description":"Narrator voice. See GET /v1/options voices tagged quiz_video.","type":"string","enum":["LcfcDJNUP1GQjkzn1xUU","EXAVITQu4vr4xnSDxMaL","pNInz6obpgDQGcFmaJgB","TxGEqnHWrfWFTfGW9XjX","JBFqnCBsd6RMkjVDRZzb","onwK4e9ZLuTAKqWW03F9"]},"theme":{"default":"neon","type":"string","enum":["neon","sunset","classic"]},"font":{"default":"anton","type":"string","enum":["anton","bangers","bebas-neue","poppins"]},"countdownSeconds":{"default":5,"anyOf":[{"type":"number","const":3},{"type":"number","const":5},{"type":"number","const":7}]},"captionsEnabled":{"default":false,"type":"boolean"},"silent":{"default":false,"description":"Render with no narration. Refused unless explicitly true.","type":"boolean"}},"required":["topic"],"additionalProperties":false,"description":"Plain-text guide for agents: https://api.ghostshorts.com/v1/templates/quiz_video.txt"},"reddit_story_inputs":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"Post title, narrated first and shown on the title card."},"text":{"type":"string","minLength":800,"maxLength":5000,"description":"The story body, 800 to 5000 characters."},"author":{"default":"Anonymous","type":"string","maxLength":32},"voiceId":{"default":"pNInz6obpgDQGcFmaJgB","description":"Narrator voice. See GET /v1/options voices tagged reddit_story.","type":"string","enum":["pNInz6obpgDQGcFmaJgB","2EiwWnXFnvU5JabPnv8n","CwhRBWXzGAHq8TQ4Fs17","EXAVITQu4vr4xnSDxMaL","FGY2WhTYpPnrIDTdsKH5","IKne3meq5aSn9XLyUdCD","JBFqnCBsd6RMkjVDRZzb","N2lVS1w4EtoT3dr4eOWO","SAz9YHcvj6GT2YYXdXww","SOYHLrjzK2X1ezoPC6cr","TX3LPaxmHKxFdv7VOQHJ","Xb7hH8MSUJpSbSDYk0k2","XrExE9yKIg1WjnnlVkGX","bIHbv24MWmeRgasZH58o","cgSgspJ2msm6clMCkdW9","cjVigY5qzO86Huf0OWal","iP95p4xoKVk53GoZ742B","nPczCjzI2devNBz1zQrb","onwK4e9ZLuTAKqWW03F9","pFZP5JQG7iQjIQuC4Bku","pqHfZKP75CvOlQylNhV4","vERmHY5ZzBsIRPzH78Rb","papd083UqIoSWdSkh3fG","zAr1POVZUrr1zkX0T94t","EIdfNdxb4fnsE39tEAB1","5Spsi3mCH9e7futpnGE5","G1QUjBCuRBbLbAmYlTgl","6wsXez7Nsh9HQSbtqwIK","SfXg52J54dixBlOl016v","Yf6I1OzNemrgGRsc8z1I","z0gdR3nhVl1Ig2kiEigL","wAWUBOIVEUw9IEUYoNzR","6XNSYkDqZ1blajSVtPok","LNzr3u01PIEDg0fRlvE7","GxxMAMfQkDlnqjpzjLHH","RBnMinrYKeccY3vaUxlZ","sZXOZNdVMeHi5J0uiQF7","8ng6vvUHRpznlJL92xmU","x5tvfc5X0Qh4cqmLpgrs","20ptB7Wn4Q2sMkAxK1qK","JaUVfDrFcfwGIsv8X2kN","iWydkXKoiVtvdn4vLKp9","HnnPtoATgzx4ubChwm24","C1DBnkwmDIzoLOPlBvSg","hIssydxXZ1WuDorjx6Ic","NacdHGUYR1k3M0FAbAia","87QJokeqzysrKtZFt7jl"]},"language":{"default":"en","description":"Narration language (ISO 639-1).","type":"string","minLength":2,"maxLength":5},"backgroundVideoAssetId":{"description":"A background video id from GET /v1/options backgrounds.assets. Mutually exclusive with backgroundCategory.","type":"string","minLength":1},"backgroundCategory":{"description":"Pick any background from this category. Mutually exclusive with backgroundVideoAssetId.","type":"string","enum":["Minecraft","Mario Kart","GTA V","Subway Surfers","Satisfying","Backrooms","Roblox"]},"backgroundVideoUrl":{"description":"Your own footage by public https URL. Video is converted for rendering (H.264 baseline, 1080x1920, cropped to fill, silent); files up to 300 MB / 10 min. Mutually exclusive with backgroundVideoAssetId and backgroundCategory.","type":"string","minLength":1,"maxLength":2048},"backgroundAudioAssetId":{"description":"Background music id from GET /v1/options music.","type":"string","minLength":1},"backgroundAudioUrl":{"description":"Your own track by public https URL. Converted to MP3 for rendering; files up to 30 MB / 10 min. Mutually exclusive with backgroundAudioAssetId.","type":"string","minLength":1,"maxLength":2048},"backgroundAudioVolume":{"default":30,"type":"integer","minimum":0,"maximum":100},"captionPresetId":{"default":"hormozi","type":"string","enum":["hormozi","beast","ali","iman","devin","matt","kendrick","laura","doug","carlos","nick","fire","neon","william","leon","luke","classicWhite"]},"captionPosition":{"default":"middle","type":"string","enum":["top","middle","bottom"]},"captionsEnabled":{"default":true,"type":"boolean"},"titleCardTheme":{"default":"dark","type":"string","enum":["dark","light"]},"voiceSpeed":{"default":1,"type":"number","minimum":0.85,"maximum":1.25},"stability":{"default":50,"type":"number","minimum":0,"maximum":100},"similarity":{"default":75,"type":"number","minimum":0,"maximum":100},"pitch":{"default":1,"type":"number","minimum":0.5,"maximum":2}},"required":["title","text"],"additionalProperties":false,"description":"Plain-text guide for agents: https://api.ghostshorts.com/v1/templates/reddit_story.txt"},"fake_text_message_inputs":{"type":"object","properties":{"platform":{"default":"imessage","description":"Chat skin. \"ios\" is accepted as an alias of \"imessage\".","type":"string","enum":["imessage","whatsapp","instagram","tinder","ios"]},"theme":{"default":"dark","description":"Light or dark chat skin.","type":"string","enum":["dark","light"]},"contact":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":40,"description":"The contact shown in the chat header."},"avatarAssetId":{"description":"An image asset you own (or a system image) for the contact avatar.","type":"string","minLength":1}},"required":["name"],"additionalProperties":false},"messages":{"minItems":1,"maxItems":80,"type":"array","items":{"type":"object","properties":{"sender":{"type":"string","enum":["me","them"],"description":"\"me\" is the phone owner (right-hand bubbles); \"them\" is the contact."},"text":{"description":"The message text. Send text or imageAssetId, not both.","type":"string","minLength":1,"maxLength":500},"imageAssetId":{"description":"An image asset you own (or a system image) shown as a photo message, held 3 seconds.","type":"string","minLength":1},"delayMs":{"description":"Gap in milliseconds before this message appears. Defaults to the pacing preset.","type":"integer","minimum":0,"maximum":10000},"timestampLabel":{"description":"Timestamp row shown above this message, e.g. \"Today 8:12 PM\".","type":"string","minLength":1,"maxLength":40},"gapLabel":{"description":"A time-gap row shown before this message, e.g. \"4 days later\". Fixed at voice-generation time (900 ms).","type":"string","minLength":1,"maxLength":40},"receipt":{"description":"Receipt label under this message, free text, e.g. \"Read 8:14 PM\" or \"Delivered\".","type":"string","minLength":1,"maxLength":30},"typingIndicatorBefore":{"description":"Show a typing bubble before this message (at least 800 ms; fixed at voice-generation time).","type":"boolean"}},"required":["sender"],"additionalProperties":false},"description":"The conversation, in order."},"pacing":{"default":"normal","description":"Default gap for messages without delayMs: fast 0 ms, normal 0 ms, slow 1000 ms.","type":"string","enum":["fast","normal","slow"]},"voices":{"default":{"enabled":true,"me":"pNInz6obpgDQGcFmaJgB","them":"EXAVITQu4vr4xnSDxMaL"},"type":"object","properties":{"enabled":{"default":true,"description":"Must be true: every Text Messages video is voiced (the bubble timing comes from the voice clips).","type":"boolean"},"me":{"default":"pNInz6obpgDQGcFmaJgB","description":"Voice for \"me\". See GET /v1/options voices tagged fake_text_message.","type":"string","enum":["pNInz6obpgDQGcFmaJgB","2EiwWnXFnvU5JabPnv8n","CwhRBWXzGAHq8TQ4Fs17","EXAVITQu4vr4xnSDxMaL","FGY2WhTYpPnrIDTdsKH5","IKne3meq5aSn9XLyUdCD","JBFqnCBsd6RMkjVDRZzb","N2lVS1w4EtoT3dr4eOWO","SAz9YHcvj6GT2YYXdXww","SOYHLrjzK2X1ezoPC6cr","TX3LPaxmHKxFdv7VOQHJ","Xb7hH8MSUJpSbSDYk0k2","XrExE9yKIg1WjnnlVkGX","bIHbv24MWmeRgasZH58o","cgSgspJ2msm6clMCkdW9","cjVigY5qzO86Huf0OWal","iP95p4xoKVk53GoZ742B","nPczCjzI2devNBz1zQrb","onwK4e9ZLuTAKqWW03F9","pFZP5JQG7iQjIQuC4Bku","pqHfZKP75CvOlQylNhV4","vERmHY5ZzBsIRPzH78Rb","papd083UqIoSWdSkh3fG","zAr1POVZUrr1zkX0T94t","EIdfNdxb4fnsE39tEAB1","5Spsi3mCH9e7futpnGE5","G1QUjBCuRBbLbAmYlTgl","6wsXez7Nsh9HQSbtqwIK","SfXg52J54dixBlOl016v","Yf6I1OzNemrgGRsc8z1I","z0gdR3nhVl1Ig2kiEigL","wAWUBOIVEUw9IEUYoNzR","6XNSYkDqZ1blajSVtPok","LNzr3u01PIEDg0fRlvE7","GxxMAMfQkDlnqjpzjLHH","RBnMinrYKeccY3vaUxlZ","sZXOZNdVMeHi5J0uiQF7","8ng6vvUHRpznlJL92xmU","x5tvfc5X0Qh4cqmLpgrs","20ptB7Wn4Q2sMkAxK1qK","JaUVfDrFcfwGIsv8X2kN","iWydkXKoiVtvdn4vLKp9","HnnPtoATgzx4ubChwm24","C1DBnkwmDIzoLOPlBvSg","hIssydxXZ1WuDorjx6Ic","NacdHGUYR1k3M0FAbAia","87QJokeqzysrKtZFt7jl"]},"them":{"default":"EXAVITQu4vr4xnSDxMaL","description":"Voice for \"them\".","type":"string","enum":["pNInz6obpgDQGcFmaJgB","2EiwWnXFnvU5JabPnv8n","CwhRBWXzGAHq8TQ4Fs17","EXAVITQu4vr4xnSDxMaL","FGY2WhTYpPnrIDTdsKH5","IKne3meq5aSn9XLyUdCD","JBFqnCBsd6RMkjVDRZzb","N2lVS1w4EtoT3dr4eOWO","SAz9YHcvj6GT2YYXdXww","SOYHLrjzK2X1ezoPC6cr","TX3LPaxmHKxFdv7VOQHJ","Xb7hH8MSUJpSbSDYk0k2","XrExE9yKIg1WjnnlVkGX","bIHbv24MWmeRgasZH58o","cgSgspJ2msm6clMCkdW9","cjVigY5qzO86Huf0OWal","iP95p4xoKVk53GoZ742B","nPczCjzI2devNBz1zQrb","onwK4e9ZLuTAKqWW03F9","pFZP5JQG7iQjIQuC4Bku","pqHfZKP75CvOlQylNhV4","vERmHY5ZzBsIRPzH78Rb","papd083UqIoSWdSkh3fG","zAr1POVZUrr1zkX0T94t","EIdfNdxb4fnsE39tEAB1","5Spsi3mCH9e7futpnGE5","G1QUjBCuRBbLbAmYlTgl","6wsXez7Nsh9HQSbtqwIK","SfXg52J54dixBlOl016v","Yf6I1OzNemrgGRsc8z1I","z0gdR3nhVl1Ig2kiEigL","wAWUBOIVEUw9IEUYoNzR","6XNSYkDqZ1blajSVtPok","LNzr3u01PIEDg0fRlvE7","GxxMAMfQkDlnqjpzjLHH","RBnMinrYKeccY3vaUxlZ","sZXOZNdVMeHi5J0uiQF7","8ng6vvUHRpznlJL92xmU","x5tvfc5X0Qh4cqmLpgrs","20ptB7Wn4Q2sMkAxK1qK","JaUVfDrFcfwGIsv8X2kN","iWydkXKoiVtvdn4vLKp9","HnnPtoATgzx4ubChwm24","C1DBnkwmDIzoLOPlBvSg","hIssydxXZ1WuDorjx6Ic","NacdHGUYR1k3M0FAbAia","87QJokeqzysrKtZFt7jl"]}},"additionalProperties":false},"background":{"default":{},"type":"object","properties":{"category":{"description":"Pick any background from this category (GET /v1/options backgrounds.categories).","type":"string","enum":["Minecraft","Mario Kart","GTA V","Subway Surfers","Satisfying","Backrooms","Roblox"]},"assetId":{"description":"A background video id from GET /v1/options backgrounds.assets, or your own upload.","type":"string","minLength":1},"ownUrl":{"description":"Your own footage by public https URL. Video is converted for rendering (H.264 baseline, 1080x1920, cropped to fill, silent); files up to 300 MB / 10 min.","type":"string","minLength":1,"maxLength":2048}},"additionalProperties":false},"music":{"default":{},"type":"object","properties":{"assetId":{"description":"Background music id from GET /v1/options music. Omit for none.","type":"string","minLength":1},"ownUrl":{"description":"Your own track by public https URL. Converted to MP3 for rendering; files up to 30 MB / 10 min.","type":"string","minLength":1,"maxLength":2048},"volume":{"description":"0-100. Default 30.","type":"integer","minimum":0,"maximum":100}},"additionalProperties":false},"captions":{"description":"Text captions pinned to messages, up to 20.","maxItems":20,"type":"array","items":{"type":"object","properties":{"afterMessage":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1-based number of the message this caption follows (at most the number of messages)."},"text":{"type":"string","minLength":1,"maxLength":120,"description":"Caption text."},"delayMs":{"default":0,"description":"Milliseconds after that message appears. Default 0.","type":"integer","minimum":0,"maximum":10000},"position":{"default":"below_card","description":"Default below_card.","type":"string","enum":["below_card","above_card"]}},"required":["afterMessage","text"],"additionalProperties":false}},"hookText":{"description":"Hook shown before the conversation.","type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":120,"description":"The hook line."},"seconds":{"default":2,"description":"How long the hook holds before the chat starts. Default 2.","type":"number","minimum":0.5,"maximum":10}},"required":["text"],"additionalProperties":false},"endCard":{"description":"End card shown after the last message.","type":"object","properties":{"line1":{"type":"string","minLength":1,"maxLength":80},"line2":{"type":"string","minLength":1,"maxLength":80},"seconds":{"default":2.5,"description":"How long the end card holds. Default 2.5.","type":"number","minimum":0.5,"maximum":10}},"required":["line1"],"additionalProperties":false},"sfx":{"description":"Message sounds, each default false. Honoured, but silent until the sound library is uploaded.","type":"object","properties":{"send":{"default":false,"type":"boolean"},"receive":{"default":false,"type":"boolean"},"typing":{"default":false,"type":"boolean"}},"additionalProperties":false},"layout":{"description":"Chat card layout.","type":"object","properties":{"style":{"default":"card_over_background","description":"The chat card over the background video.","type":"string","enum":["card_over_background"]},"cardWidthPct":{"default":80,"description":"Card width as a percentage of the frame. Default 80.","type":"integer","minimum":40,"maximum":100},"cardHeight":{"default":"fit","description":"fit (default): the card grows with the conversation. full: fixed-height card, the wizard's legacy look.","type":"string","enum":["full","fit"]}},"additionalProperties":false},"cutAfterMessage":{"description":"1-based message number. The video reports cutAtMs: where message N+1 appears (or the end of the video when N is the last message), so you can split the MP4 there.","type":"integer","minimum":1,"maximum":80}},"required":["contact","messages"],"additionalProperties":false,"description":"Plain-text guide for agents: https://api.ghostshorts.com/v1/templates/fake_text_message.txt"},"Problem":{"type":"object","description":"RFC 9457 problem details. type is https://ghostshorts.com/developers/errors#<code>.","required":["type","title","status","detail","code"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"code":{"type":"string"},"upsell":{"type":"string","enum":["subscribe","topup","none"],"description":"Present on 402."},"plansUrl":{"type":"string","format":"uri","description":"Informational pricing page. Present on 402."},"errors":{"type":"array","description":"Validation issues on INVALID_INPUT.","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"},"code":{"type":"string"}}}}}},"Me":{"type":"object","required":["user","plan","entitlement","usage","limits"],"properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"}}},"plan":{"type":"object","properties":{"tier":{"type":["string","null"]},"pricingV2":{"type":"boolean"}}},"entitlement":{"type":"object","description":"The same entitlement struct the GhostShorts app gates on.","properties":{"canUseTemplates":{"type":"boolean"},"upsell":{"type":"string","enum":["subscribe","topup","none"]},"credits":{"type":"object"},"plan":{"type":"object"}}},"usage":{"type":"object"},"limits":{"type":"object","properties":{"requestsPerMinute":{"type":"integer"},"createsPerMinute":{"type":"integer"},"freeToolsPerHour":{"type":"integer"},"freeToolsPerDay":{"type":"integer"},"concurrentRenders":{"type":"integer"},"mediaImportsPerDay":{"type":"integer","description":"New own-media URLs (background.ownUrl, music.ownUrl) fetched per day. A URL already imported in the last 30 days is reused and does not count."}}},"webhooks":{"type":"object","description":"How to verify webhook deliveries.","properties":{"signingSecret":{"type":"string","description":"whsec_<hex>. HMAC key = the hex part."},"signatureHeader":{"type":"string"},"scheme":{"type":"string"}}},"key":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"}}}}},"Template":{"type":"object","required":["id","name","description","inputSchema","defaults","freeTools","paidSteps","notes"],"properties":{"id":{"type":"string","enum":["article_video","quiz_video","reddit_story","fake_text_message"]},"name":{"type":"string"},"description":{"type":"string"},"inputSchema":{"type":"object","description":"JSON Schema (2020-12) for inputs."},"defaults":{"type":"object"},"freeTools":{"type":"array","items":{"type":"string"}},"paidSteps":{"type":"array","items":{"type":"object","properties":{"step":{"type":"string"},"credits":{"type":"string"}}}},"notes":{"type":"array","items":{"type":"string"}}}},"Options":{"type":"object","properties":{"voices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"gender":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"language":{"type":"string"},"previewUrl":{"type":["string","null"]},"templates":{"type":"array","items":{"type":"string"}}}}},"backgrounds":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":["string","null"]},"thumbnailUrl":{"type":"string"},"previewUrl":{"type":"string"}}}}}},"music":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":["string","null"]}}}},"captionPresets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"aspectRatios":{"type":"array","items":{"type":"string"}},"textMessages":{"type":"object","description":"fake_text_message choices. Voices are the entries of voices tagged fake_text_message.","properties":{"platforms":{"type":"array","items":{"type":"string"}},"themes":{"type":"array","items":{"type":"string"}},"pacing":{"type":"object","additionalProperties":{"type":"integer"},"description":"Default gap in ms before a message without delayMs."},"defaultVoices":{"type":"object","properties":{"me":{"type":"string"},"them":{"type":"string"}}}}}}},"EstimateRequest":{"oneOf":[{"type":"object","required":["template","inputs"],"additionalProperties":false,"properties":{"template":{"const":"article_video"},"inputs":{"$ref":"#/components/schemas/article_video_inputs"}}},{"type":"object","required":["template","inputs"],"additionalProperties":false,"properties":{"template":{"const":"quiz_video"},"inputs":{"$ref":"#/components/schemas/quiz_video_inputs"}}},{"type":"object","required":["template","inputs"],"additionalProperties":false,"properties":{"template":{"const":"reddit_story"},"inputs":{"$ref":"#/components/schemas/reddit_story_inputs"}}},{"type":"object","required":["template","inputs"],"additionalProperties":false,"properties":{"template":{"const":"fake_text_message"},"inputs":{"$ref":"#/components/schemas/fake_text_message_inputs"}}}]},"CreateVideoRequest":{"type":"object","required":["template","inputs"],"properties":{"template":{"type":"string","enum":["article_video","quiz_video","reddit_story","fake_text_message"],"description":"Templates available for videos."},"inputs":{"oneOf":[{"$ref":"#/components/schemas/article_video_inputs"},{"$ref":"#/components/schemas/quiz_video_inputs"},{"$ref":"#/components/schemas/reddit_story_inputs"},{"$ref":"#/components/schemas/fake_text_message_inputs"}],"description":"The input schema of the chosen template."},"projectId":{"type":"string","description":"A project from POST /v1/scripts; its script (article_video) or pack (quiz_video) is reused and that step is skipped. Option fields in inputs (voice, theme, font, ...) still apply. For reddit_story, a project from POST /v1/previews: inputs are still required in full and applied, and the full narration is always generated (the teaser is never reused). fake_text_message takes no projectId."},"webhookUrl":{"type":"string","format":"uri","description":"https only; must resolve to a public address. Receives video.completed / video.failed."}}},"ScriptRequest":{"oneOf":[{"type":"object","required":["template","inputs"],"properties":{"template":{"const":"article_video"},"inputs":{"type":"object","required":["source"],"description":"source, length and tone from the article_video input schema.","properties":{"source":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"url"},"url":{"type":"string","format":"uri","description":"Public article, blog or newsletter URL."}},"required":["kind","url"]},{"type":"object","properties":{"kind":{"type":"string","const":"text"},"text":{"type":"string","minLength":1,"maxLength":60000,"description":"Pasted article text. The script step needs 90 to 4000 words."}},"required":["kind","text"]}],"description":"Where the article comes from: a URL we fetch, or the text itself."},"length":{"default":"standard","description":"short ~30s, standard ~60s, deep ~90s.","type":"string","enum":["short","standard","deep"]},"tone":{"default":"explainer","type":"string","enum":["explainer","punchy","authority"]}}}}},{"type":"object","required":["template","inputs"],"properties":{"template":{"const":"quiz_video"},"inputs":{"type":"object","required":["topic"],"description":"topic, difficulty, questionCount, categoryId and videoTitle from the quiz_video input schema.","properties":{"topic":{"type":"string","minLength":2,"maxLength":200,"description":"What the quiz is about, e.g. \"the Roman Empire\"."},"difficulty":{"default":"medium","type":"string","enum":["easy","medium","hard"]},"questionCount":{"default":5,"anyOf":[{"type":"number","const":5},{"type":"number","const":7},{"type":"number","const":10}]},"categoryId":{"description":"Optional steer for the generator.","type":"string","enum":["general-knowledge","geography","history","science","movies-tv","music","sports","food-drink","video-games","logos-brands"]},"videoTitle":{"description":"Title on the intro card.","type":"string","minLength":1,"maxLength":48}}}}}]},"PreviewRequest":{"type":"object","required":["template","inputs"],"properties":{"template":{"const":"reddit_story"},"inputs":{"type":"object","required":["title","text"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"text":{"type":"string","minLength":5,"maxLength":5000,"description":"The story. Only the first ~170 characters are narrated in the teaser."},"voiceId":{"type":"string","description":"A reddit_story voice from GET /v1/options. Default Adam."},"language":{"type":"string","default":"en"}}},"projectId":{"type":"string","description":"Reuse one of your reddit_story drafts (returns its stored teaser if it already has one)."}}},"Preview":{"type":"object","required":["projectId","template","audioUrl","captions","durationMs","truncated","notices"],"properties":{"projectId":{"type":"string"},"template":{"const":"reddit_story"},"audioUrl":{"type":"string","format":"uri","description":"Signed MP3 URL, valid 12 hours."},"captions":{"type":"array","description":"Word captions of the story part (after the title), in ms.","items":{"type":"object","properties":{"id":{"type":"string"},"startTime":{"type":"number"},"endTime":{"type":"number"},"displayText":{"type":"string"}}}},"durationMs":{"type":"integer"},"truncated":{"type":"boolean","description":"True when the story was cut to the teaser length."},"notices":{"type":"array","items":{"type":"string"}}}},"Script":{"type":"object","required":["projectId","template"],"description":"article_video answers with script/segments/source/truncated; quiz_video answers with pack. Both carry notices: show them to the user.","properties":{"projectId":{"type":"string"},"template":{"type":"string","enum":["article_video","quiz_video"]},"script":{"type":"object","description":"article_video: headline, narration and the rest of the generated script."},"pack":{"type":"object","description":"quiz_video: the generated pack. Answers are AI-generated: have the user check them before creating the video.","properties":{"title":{"type":"string"},"topic":{"type":"string"},"difficulty":{"type":"string","enum":["easy","medium","hard"]},"questions":{"type":"array","items":{"type":"object","required":["index","question","options","correctIndex"],"properties":{"index":{"type":"integer","description":"1-based."},"question":{"type":"string"},"options":{"type":"array","items":{"type":"string"},"minItems":4,"maxItems":4},"correctIndex":{"type":"integer","minimum":0,"maximum":3,"description":"0-based index into options."},"fact":{"type":"string","description":"One-line explanation shown on the reveal."}}}}}},"segments":{"type":"array","items":{"type":"object"}},"source":{"type":"object","description":"What was extracted from the source (title, site, word count)."},"truncated":{"type":"boolean"},"notices":{"type":"array","items":{"type":"string"}}}},"Video":{"type":"object","required":["id","template","status","stage","createdAt"],"properties":{"id":{"type":"string"},"template":{"type":"string"},"status":{"type":"string","enum":["queued","preparing_media","generating_script","narrating","rendering","completed","failed","cancelled"],"description":"preparing_media: fetching and converting your own media (ownUrl fields), before anything is charged. Its failures are error.code MEDIA_FETCH_FAILED, MEDIA_INVALID, MEDIA_TOO_LONG or MEDIA_QUOTA_EXCEEDED, with creditsCharged null."},"stage":{"type":"string","description":"Human-readable step, e.g. \"Rendering video\"."},"progress":{"type":["integer","null"],"minimum":0,"maximum":100,"description":"Render progress while rendering; 100 when completed."},"projectId":{"type":["string","null"]},"exportId":{"type":["string","null"]},"downloadUrl":{"type":["string","null"],"format":"uri","description":"Public link (no auth), valid 7 days after completion. Give it to the user exactly as returned."},"downloadExpiresAt":{"type":["string","null"],"format":"date-time"},"error":{"type":["object","null"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}},"creditsCharged":{"type":["object","null"],"description":"What was actually charged: { narration: credits, render: credits, videos: allowance videos }."},"estimatedSecondsRemaining":{"type":["integer","null"]},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"cutAtMs":{"type":["integer","null"],"minimum":0,"description":"fake_text_message with cutAfterMessage: the millisecond in the MP4 where message N+1 appears (the end of the video when N is the last message), set once the render is queued. null otherwise."},"notices":{"type":"array","items":{"type":"string"},"description":"Template notices to show the user (quiz_video: answers are AI-generated, check them before publishing)."}}},"Estimate":{"type":"object","required":["template","credits","videos","pricingModel","assumptions"],"properties":{"template":{"type":"string"},"credits":{"type":"object","properties":{"narration":{"type":"integer"},"render":{"type":"integer"},"total":{"type":"integer"}}},"videos":{"type":"integer"},"pricingModel":{"type":"string","enum":["v2","legacy"]},"assumptions":{"type":"array","items":{"type":"string"}}}}}}}