{"openapi":"3.1.0","info":{"title":"Animatra API","version":"1","description":"Generate clips, build animated stories with consistent characters, and record narrated tutorials. Every request runs inside one organization: the one the API key belongs to. Every dollar figure is what your organization is charged, in credits, so an estimate names what the work takes off your balance."},"servers":[{"url":"/api/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An organization API key from Organization settings → API, sent as `Authorization: Bearer anm_…` (or as an `x-api-key` header). Each key carries scopes chosen when it is created; a call outside them returns 403 naming the scope it needs."}}},"tags":[{"name":"Clips","description":"Single text-to-video clips."},{"name":"Stories","description":"Story bibles: premise, style guide, cast, shots, animatic and film."},{"name":"Characters","description":"A story's cast, each with a four-angle turnaround sheet."},{"name":"Shots","description":"A story's shots: keyframe first, then animation."},{"name":"Tutorials","description":"Narrated screen recordings of a real website."},{"name":"Spend","description":"What the organization has spent this month."},{"name":"Webhooks","description":"Endpoints that receive events as they happen."},{"name":"Reference","description":"Providers, voices, the organization."},{"name":"Writing","description":"Redrafting the words a film is made from."}],"paths":{"/generations":{"post":{"tags":["Clips"],"summary":"Create a clip","description":"Queues a text-to-video clip. Returns immediately; poll `GET /generations/{id}` or subscribe to `clip.completed`. Refused with 402 when the estimate would pass the organization's monthly ceiling.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string","minLength":3,"maxLength":2000},"durationSec":{"default":5,"type":"number","enum":[4,5,6,7,8,9,10,11,12]},"aspectRatio":{"default":"16:9","type":"string","enum":["16:9","9:16","1:1"]},"stylePreset":{"type":"string","enum":["cartoon","anime","3d-animated","claymation","stop-motion","watercolor","storybook","pixel-art","comic","cinematic","whiteboard"]},"providerId":{"type":"string"}},"required":["prompt","durationSec","aspectRatio"],"additionalProperties":false}}}},"responses":{"201":{"description":"Queued","content":{"application/json":{"example":{"id":"…","status":"queued","estimatedCostUsd":0.88}}}}}},"get":{"tags":["Clips"],"summary":"List generations","parameters":[{"name":"kind","in":"query","schema":{"type":"string","enum":["clip","keyframe","final","tutorial","narration","music"]}},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","running","succeeded","failed"]}},{"name":"q","in":"query","schema":{"type":"string"},"description":"Matches the prompt."},{"name":"providerId","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":24}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"`nextCursor` from the previous page."}],"responses":{"200":{"description":"A page, newest first","content":{"application/json":{"example":{"generations":[{"id":"6fe04814-85c9-4eae-a9b0-36b981e1bb57","kind":"clip","prompt":"A paper boat drifting down a rain-streaked window, morning light","status":"succeeded","providerId":"fal-kling-v2.5-turbo-pro","estimatedCostUsd":0.88,"actualCostUsd":0.88,"durationSec":5,"createdAt":"2026-09-15T04:00:15.000Z","playbackUrl":"https://…signed, valid for one hour…","thumbnailUrl":"https://…signed…"}],"nextCursor":null}}}}}},"delete":{"tags":["Clips"],"summary":"Delete generations","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed","content":{"application/json":{"example":{"deleted":2}}}}}}},"/generations/{id}":{"get":{"tags":["Clips"],"summary":"Get a generation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Generation id"}],"responses":{"200":{"description":"The generation with playback URLs","content":{"application/json":{"example":{"id":"6fe04814-85c9-4eae-a9b0-36b981e1bb57","kind":"clip","prompt":"A paper boat drifting down a rain-streaked window, morning light","status":"succeeded","providerId":"fal-kling-v2.5-turbo-pro","estimatedCostUsd":0.88,"actualCostUsd":0.88,"durationSec":5,"createdAt":"2026-09-15T04:00:15.000Z","playbackUrl":"https://…signed, valid for one hour…","thumbnailUrl":"https://…signed…"}}}}}},"patch":{"tags":["Clips"],"summary":"Rewrite a clip's prompt before it is made","description":"Works while a clip is queued, running or didn't complete; a clip that is already made is answered with 409, since its prompt is the record of what was made. Follow this with `POST /generations/{id}/retry` to run it with the new words. Every other kind of generation composes its prompt from the shot, character or script it belongs to, and is answered with 409 naming where to edit it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Generation id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string"}},"required":["prompt"],"additionalProperties":false}}}},"responses":{"200":{"description":"The generation"}}}},"/generations/{id}/retry":{"post":{"tags":["Clips"],"summary":"Run a generation again","description":"For one that didn't complete, and for one still queued or running after ten minutes, which only happens when the pipeline wasn't listening.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Generation id"}],"responses":{"200":{"description":"Queued again","content":{"application/json":{"example":{"id":"…","status":"queued"}}}}}}},"/story-bibles":{"post":{"tags":["Stories"],"summary":"Start a story","description":"Claude expands the premise into a logline, visual style, tone, setting and palette before this returns.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"premise":{"type":"string","minLength":10,"maxLength":2000},"stylePreset":{"type":"string","enum":["cartoon","anime","3d-animated","claymation","stop-motion","watercolor","storybook","pixel-art","comic","cinematic","whiteboard"]}},"required":["title","premise"],"additionalProperties":false}}}},"responses":{"201":{"description":"The story bible"}}},"get":{"tags":["Stories"],"summary":"List stories","responses":{"200":{"description":"Stories with counts and a cover","content":{"application/json":{"example":{"storyBibles":[]}}}}}},"delete":{"tags":["Stories"],"summary":"Delete stories and everything inside them","description":"Shots, keyframes, animatics and films are deleted. Characters are kept with their drawings: their `storyBibleId` becomes null, and `characters/{id}/copy` moves them into any story at no charge.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed"}}}},"/story-bibles/{id}":{"get":{"tags":["Stories"],"summary":"Get a story with its renders","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"responses":{"200":{"description":"Story, animatic and film URLs, share tokens"}}},"patch":{"tags":["Stories"],"summary":"Edit title, premise, logline, style guide, palette or music level","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string"},"premise":{"description":"The story in your own words. Every shot list and script drafted afterwards reads it; the logline and style guide stay as they are until `story-bibles/{id}/bible` writes them again.","type":"string"},"logline":{"type":"string"},"styleGuide":{"type":"object","properties":{"visualStyle":{"type":"string"},"tone":{"type":"string"},"setting":{"type":"string"}},"required":["visualStyle","tone","setting"],"additionalProperties":false},"palette":{"type":"array","items":{"type":"string"}},"musicLevel":{"description":"How loud the score sits under the narration in the final film. At 0 the film is rendered with no music, which also saves composing it.","type":"integer","minimum":0,"maximum":100}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated story"}}}},"/story-bibles/{id}/bible":{"post":{"tags":["Stories"],"summary":"Write the logline, style guide and palette from the premise","description":"For a premise edited into a different story, where the logline and style guide still describe the story the film used to be. Nothing already drawn, animated or recorded changes: the next keyframe follows the new style. `?estimate=1` returns the cost without writing anything.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"responses":{"200":{"description":"The updated story"}}}},"/story-bibles/{id}/shot-list":{"post":{"tags":["Stories"],"summary":"Draft a shot list with Claude","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotCount":{"default":6,"type":"integer","minimum":1,"maximum":12}},"required":["shotCount"],"additionalProperties":false}}}},"responses":{"202":{"description":"Drafting; shots appear within seconds"}}}},"/story-bibles/{id}/shots/keyframe-all":{"post":{"tags":["Stories"],"summary":"Draw a keyframe for every draft shot, or for the selected shots","description":"Without `shotIds`, draws every draft shot. With `shotIds`, draws each selected shot that is not being worked on, which draws again the ones that already have a keyframe; those go back to review and keep their earlier drawings in history. Add `?estimate=1` to get the count, how many are drawn again, and the cost without queuing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotIds":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"example":{"queued":6,"estimatedCostUsd":0.58}}}}}}},"/story-bibles/{id}/shots/approve-script-all":{"post":{"tags":["Stories"],"summary":"Approve the words of every shot","description":"Approves the description and spoken lines of every shot whose script has not been approved yet, or only the shots given in `shotIds`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotIds":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Count approved","content":{"application/json":{"example":{"approved":6}}}}}}},"/story-bibles/{id}/shots/approve-all":{"post":{"tags":["Stories"],"summary":"Approve every keyframe waiting for review, or the selected ones","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotIds":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Approved","content":{"application/json":{"example":{"approved":4}}}}}}},"/story-bibles/{id}/shots/animate-all":{"post":{"tags":["Stories"],"summary":"Animate every shot with an approved keyframe, or the selected shots","description":"Without `shotIds`, animates every shot with an approved keyframe. With `shotIds`, animates each selected shot that is approved or already animated, which animates the latter again. Add `?estimate=1` to price it without queuing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotIds":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Queued"}}}},"/story-bibles/{id}/shots/reorder":{"post":{"tags":["Stories"],"summary":"Reorder shots","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shotIds":{"type":"array","items":{"type":"string"}}},"required":["shotIds"],"additionalProperties":false}}}},"responses":{"200":{"description":"Reordered"}}}},"/story-bibles/{id}/animatic":{"post":{"tags":["Stories"],"summary":"Assemble the animatic","description":"Assembles the animatic. Shots nobody has changed are joined again from the pieces the last assembly kept, so only what has changed is built and paid for. Add `?estimate=1` to get the cost, the number of shots that would be built (`changed`) and their ids, without starting it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"responses":{"202":{"description":"Assembling; `animatic.ready` follows"}}}},"/story-bibles/{id}/animatic/approve":{"post":{"tags":["Stories"],"summary":"Approve the animatic","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"responses":{"200":{"description":"Stage is now animatic_approved"}}}},"/story-bibles/{id}/render":{"post":{"tags":["Stories"],"summary":"Render the final film","description":"Renders the film, and renders a finished film again after a trim, a reorder or a change of music level. Only the shots that have changed are built; the score is used again unless `freshMusic` asks for a new one. Add `?estimate=1` to get the cost, the number of shots that would be built (`changed`) and whether the score is being used again (`musicReused`), without starting it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"freshMusic":{"description":"Compose a new score instead of using the one this story already has.","type":"boolean"}},"additionalProperties":false}}}},"responses":{"202":{"description":"Rendering; `film.ready` follows"}}}},"/story-bibles/{id}/share/{kind}":{"post":{"tags":["Stories"],"summary":"Share a render publicly","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"},{"name":"kind","in":"path","required":true,"schema":{"type":"string"},"description":"`animatic` or `film`"}],"responses":{"200":{"description":"A token; the page is /watch/{token}","content":{"application/json":{"example":{"shareToken":"…"}}}}}},"delete":{"tags":["Stories"],"summary":"Stop sharing","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"},{"name":"kind","in":"path","required":true,"schema":{"type":"string"},"description":"`animatic` or `film`"}],"responses":{"200":{"description":"Sharing off"}}}},"/story-bibles/{id}/shorts":{"post":{"tags":["Stories"],"summary":"Cut a short from the film","description":"A vertical (9:16, for YouTube Shorts, TikTok and Reels) or square (1:1) cut of the finished film with captions in the chosen style. ffmpeg only, so it spends nothing; `short.ready` follows and the short appears in `GET /story-bibles/{id}` under `shorts`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"format":{"default":"9:16","type":"string","enum":["9:16","1:1"]},"framing":{"default":"blur","type":"string","enum":["blur","crop"]},"captionStyle":{"default":"bold","type":"string","enum":["bold","classic","clean","none"]}},"required":["format","framing","captionStyle"],"additionalProperties":false}}}},"responses":{"202":{"description":"Cutting","content":{"application/json":{"example":{"renderId":"…","format":"9:16"}}}}}},"delete":{"tags":["Stories"],"summary":"Delete shorts","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Story id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed"}}}},"/characters":{"post":{"tags":["Characters"],"summary":"Add a character","description":"Claude writes the visual description, then four turnaround angles are drawn; `character.ready` follows. Send multipart form data with a `reference` file (a drawing or a photo) and both the description and every angle are drawn to look like it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"storyBibleId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":100},"idea":{"type":"string","minLength":3,"maxLength":1000}},"required":["storyBibleId","name","idea"],"additionalProperties":false}},"multipart/form-data":{"schema":{"type":"object","properties":{"storyBibleId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":100},"idea":{"type":"string","minLength":3,"maxLength":1000},"reference":{"type":"string","format":"binary","description":"A drawing or a photo of the character: PNG, JPEG or WebP, up to 8 MB."}},"required":["storyBibleId","name","idea"]}}}},"responses":{"201":{"description":"The character"}}},"get":{"tags":["Characters"],"summary":"List characters","description":"Without `storyBibleId`, lists every character in the organization, including characters whose story was deleted (`storyBibleId` is null).","parameters":[{"name":"storyBibleId","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Characters with turnaround image URLs"}}},"delete":{"tags":["Characters"],"summary":"Delete characters","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed"}}}},"/characters/{id}":{"patch":{"tags":["Characters"],"summary":"Rename, change the voice or the description","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string"},"voiceId":{"type":["string","null"]},"description":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"The character"}}}},"/characters/{id}/reference":{"put":{"tags":["Characters"],"summary":"Add or replace the reference image","description":"The turnaround is redrawn from the new image only when you call `regenerate-refs` afterwards.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"reference":{"type":"string","format":"binary","description":"A drawing or a photo of the character: PNG, JPEG or WebP, up to 8 MB."}},"required":["reference"]}}}},"responses":{"200":{"description":"The character"}}},"delete":{"tags":["Characters"],"summary":"Remove the reference image","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"responses":{"200":{"description":"The character"}}}},"/characters/{id}/approve":{"post":{"tags":["Characters"],"summary":"Approve the turnaround","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"responses":{"200":{"description":"Approved"}}}},"/characters/{id}/regenerate-refs":{"post":{"tags":["Characters"],"summary":"Redraw the turnaround","description":"Refused with `character_not_in_story` for a character whose story was deleted, because the drawing follows a story's style guide. Bring the character into a story first.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"responses":{"200":{"description":"Drawing again"}}}},"/characters/{id}/ref-sets":{"get":{"tags":["Characters"],"summary":"Kept sets of drawings","description":"The sets of angles kept for the character, newest first: the one in use and up to two earlier ones. A drawing waiting for a safety review has no links.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"responses":{"200":{"description":"Sets, each with its angles"}}}},"/characters/{id}/ref-sets/use":{"post":{"tags":["Characters"],"summary":"Put an earlier set of drawings back in use","description":"Pass the `generationIds` of a complete set from `ref-sets`. The character goes back to review, and keyframes already drawn stay as they are. Refused while the character is being drawn or while a drawing in the set waits for a safety review.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generationIds":{"type":"array","items":{"type":"string"}}},"required":["generationIds"],"additionalProperties":false}}}},"responses":{"200":{"description":"The character"}}}},"/characters/{id}/copy":{"post":{"tags":["Characters"],"summary":"Bring this character into another story","description":"Copies the character, the angles already drawn for them and any reference image into the story you name, so the same person can appear in several films. A character whose story was deleted (`storyBibleId` is null) is moved into the story instead of copied, and the answer is 200 rather than 201. Nothing is drawn again and nothing is charged.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"storyBibleId":{"type":"string"}},"required":["storyBibleId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The character, moved into the story"},"201":{"description":"The character in its new story"}}}},"/characters/{id}/revise":{"post":{"tags":["Characters"],"summary":"Change the description in your own words","description":"Say what should change about the character and the description is rewritten to match, leaving the rest of it alone. Every angle and every keyframe is drawn from that description, so call `regenerate-refs` afterwards to draw the turnaround again.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Character id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"changes":{"type":"string"}},"required":["changes"],"additionalProperties":false}}}},"responses":{"200":{"description":"The character, with the new description"}}}},"/redraft":{"post":{"tags":["Writing"],"summary":"Redraft a piece of writing","description":"Rewrites a character description, a premise, a logline, a shot, a clip prompt or a line of narration, keeping what it says and changing only how well it is written. `kind` names which of those it is; `context` is anything that keeps the redraft true to the film it belongs to.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string"},"text":{"type":"string"},"context":{"type":"string"}},"required":["kind","text"],"additionalProperties":false}}}},"responses":{"200":{"description":"The redrafted text, and what the call cost"}}}},"/shots":{"post":{"tags":["Shots"],"summary":"Add a shot","description":"`index` is the position the shot takes, counted from 0; the shots from there on move down one. An index past the end puts it last.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"storyBibleId":{"type":"string","minLength":1},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"prompt":{"type":"string","minLength":3,"maxLength":2000},"durationSec":{"default":5,"type":"number","enum":[4,5,6,7,8,9,10,11,12]},"characterIds":{"default":[],"maxItems":4,"type":"array","items":{"type":"string","minLength":1}},"narrationLines":{"default":[],"maxItems":6,"type":"array","items":{"type":"object","properties":{"characterId":{"type":"string","minLength":1},"text":{"type":"string","minLength":1,"maxLength":500}},"required":["characterId","text"],"additionalProperties":false}},"cameraMotion":{"default":null,"anyOf":[{"type":"string","enum":["static","push-in","pull-out","pan-left","pan-right","tilt-up","tilt-down","orbit","follow","crane-up","handheld"]},{"type":"null"}]},"lipSync":{"default":false,"type":"boolean"}},"required":["storyBibleId","index","prompt","durationSec","characterIds","narrationLines","cameraMotion","lipSync"],"additionalProperties":false}}}},"responses":{"201":{"description":"The shot"}}},"get":{"tags":["Shots"],"summary":"List shots","parameters":[{"name":"storyBibleId","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Shots in story order with keyframe and video URLs"}}},"delete":{"tags":["Shots"],"summary":"Delete shots","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed"}}}},"/shots/{id}":{"patch":{"tags":["Shots"],"summary":"Edit prompt, length, cast, narration, camera, model or lip-sync","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string"},"durationSec":{"type":"number","enum":[4,5,6,7,8,9,10,11,12]},"characterIds":{"type":"array","items":{"type":"string"}},"narrationLines":{"type":"array","items":{"type":"object","properties":{"characterId":{"type":"string"},"text":{"type":"string"}},"required":["characterId","text"],"additionalProperties":false}},"cameraMotion":{"description":"A camera-motion preset; null lets the model choose.","anyOf":[{"type":"string","enum":["static","push-in","pull-out","pan-left","pan-right","tilt-up","tilt-down","orbit","follow","crane-up","handheld"]},{"type":"null"}]},"videoProviderId":{"description":"The video model to animate this shot with; null picks the cheapest that takes the shot's length.","type":["string","null"]},"lipSync":{"description":"Redraw the speaker's mouth to match the narration when the film renders.","type":"boolean"},"trimStartMs":{"description":"Milliseconds cut from the start of this shot's clip.","type":"integer","minimum":0,"maximum":9007199254740991},"trimEndMs":{"description":"The millisecond the shot stops at, or null to run to the end of its clip. Both are held inside the clip's own length, keeping at least one second.","anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The shot"}}}},"/shots/{id}/approve-script":{"post":{"tags":["Shots"],"summary":"Approve a shot's words","description":"Marks this shot's description and spoken lines as read and approved, as they stand right now. A shot cannot be animated until this is set, and editing either the description or a line takes it back.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"The shot"}}},"delete":{"tags":["Shots"],"summary":"Take back a shot's approval","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"The shot"}}}},"/shots/{id}/lines":{"get":{"tags":["Shots"],"summary":"A shot's lines and their recordings","description":"Each spoken line with a link to its recording, or null where those words in that voice have never been spoken.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"The lines","content":{"application/json":{"example":{"lines":[{"characterId":"…","text":"Hello!","url":"https://…","own":false}]}}}}}}},"/shots/{id}/lines/{index}/recording":{"post":{"tags":["Shots"],"summary":"Keep your own recording of one line","description":"Uploads a recording of this line in somebody's own voice as multipart form data, in the field `recording` (MP3, M4A, WAV, WebM or OGG, up to 10 MB). It is used wherever that line is spoken, whatever voice the character has, until the words themselves change.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"},{"name":"index","in":"path","required":true,"schema":{"type":"string"},"description":"Which line of the shot, counting from zero"}],"responses":{"200":{"description":"The recording","content":{"application/json":{"example":{"url":"https://…","own":true}}}}}},"delete":{"tags":["Shots"],"summary":"Remove your own recording of one line","description":"The character's own voice speaks the line again from the next assembly.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"},{"name":"index","in":"path","required":true,"schema":{"type":"string"},"description":"Which line of the shot, counting from zero"}],"responses":{"200":{"description":"How many recordings were removed","content":{"application/json":{"example":{"removed":1}}}}}}},"/shots/{id}/lines/draft":{"post":{"tags":["Shots"],"summary":"Write a shot's lines with Claude","description":"Writes what the people in this shot say, from the shot's description and the story around it. Replaces the lines the shot has, and takes back its script approval. Add `?estimate=1` to price it without writing anything.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"The shot, with its new lines"}}}},"/shots/{id}/lines/speak":{"post":{"tags":["Shots"],"summary":"Record a shot's lines","description":"Records the shot's lines so they can be heard before anything is drawn or animated. The animatic and the film reuse these same recordings, so a line is never paid for twice. Add `?estimate=1` to price it without recording.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"202":{"description":"Recording","content":{"application/json":{"example":{"id":"…","estimatedCostUsd":0.01}}}}}}},"/shots/{id}/keyframe/change":{"post":{"tags":["Shots"],"summary":"Change one thing about a shot's picture","description":"Draws the shot's picture again starting from the one it has, keeping the characters, poses, framing, colours and background, and changing only what `change` asks for. One picture's cost, and the shot goes back to review. Add `?estimate=1` to price it without drawing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"change":{"type":"string","description":"What should change, in your own words"}},"required":["change"],"additionalProperties":false}}}},"responses":{"202":{"description":"Drawing","content":{"application/json":{"example":{"id":"…","estimatedCostUsd":0.1}}}}}}},"/shots/{id}/end-keyframe":{"post":{"tags":["Shots"],"summary":"Draw the picture a shot ends on","description":"Draws the frame the animation arrives at, continuing from the shot's keyframe (one image's cost). Add `?estimate=1` to get the cost without drawing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"202":{"description":"Drawing","content":{"application/json":{"example":{"id":"…","estimatedCostUsd":0.1}}}}}},"delete":{"tags":["Shots"],"summary":"Forget the picture a shot ends on","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"The shot"}}}},"/shots/{id}/keyframe":{"post":{"tags":["Shots"],"summary":"Draw the keyframe","description":"Draws one keyframe, or two or four candidates to choose between with `POST /shots/{id}/keyframe/use`. A shot that already has a keyframe or a video can be drawn again: it goes back to review and keeps what it had in history. Add `?estimate=1` to get the cost without drawing.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"count":{"anyOf":[{"type":"number","const":1},{"type":"number","const":2},{"type":"number","const":4}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"example":{"id":"…","status":"keyframe_queued","count":4,"estimatedCostUsd":0.4}}}}}}},"/shots/{id}/approve-keyframe":{"post":{"tags":["Shots"],"summary":"Approve the keyframe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"Approved"}}}},"/shots/{id}/animate":{"post":{"tags":["Shots"],"summary":"Animate the approved keyframe","description":"An already animated shot can be animated again; the earlier video stays in its history. Add `?estimate=1` to get the cost without animating.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"responses":{"200":{"description":"Queued"}}}},"/shots/{id}/keyframe/use":{"post":{"tags":["Shots"],"summary":"Pin an earlier keyframe from history","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Shot id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"generationId":{"type":"string"}},"required":["generationId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The shot"}}}},"/generations/history":{"get":{"tags":["Shots"],"summary":"A shot's or character's generation history","parameters":[{"name":"shotId","in":"query","schema":{"type":"string"}},{"name":"characterId","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Generations, newest first, each with its assets"}}}},"/tutorial-scripts":{"post":{"tags":["Tutorials"],"summary":"Create a tutorial script","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"targetUrl":{"type":"string","format":"uri"},"steps":{"minItems":1,"maxItems":60,"type":"array","items":{"type":"object","properties":{"action":{"type":"string","enum":["goto","click","type","hover","scroll","wait"]},"selector":{"type":"string","minLength":1,"maxLength":500},"value":{"type":"string","minLength":1,"maxLength":2000},"narration":{"type":"string","minLength":1,"maxLength":1000}},"required":["action","narration"],"additionalProperties":false}},"voiceId":{"type":"string","minLength":1}},"required":["title","targetUrl","steps"],"additionalProperties":false}}}},"responses":{"201":{"description":"The script"}}},"get":{"tags":["Tutorials"],"summary":"List scripts","responses":{"200":{"description":"Scripts"}}},"delete":{"tags":["Tutorials"],"summary":"Delete scripts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}}},"required":["ids"]}}}},"responses":{"200":{"description":"Count removed"}}}},"/tutorial-scripts/draft":{"post":{"tags":["Tutorials"],"summary":"Draft steps from an outline with Claude","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"targetUrl":{"type":"string","format":"uri"},"outline":{"type":"string"},"title":{"type":"string"}},"required":["targetUrl","outline"],"additionalProperties":false}}}},"responses":{"200":{"description":"A title and steps to edit before saving"}}}},"/tutorial-scripts/{id}":{"get":{"tags":["Tutorials"],"summary":"Get a script","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Script id"}],"responses":{"200":{"description":"The script"}}},"patch":{"tags":["Tutorials"],"summary":"Edit a script","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Script id"}],"responses":{"200":{"description":"The script"}}}},"/tutorial-scripts/{id}/run":{"post":{"tags":["Tutorials"],"summary":"Record the tutorial","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Script id"}],"responses":{"201":{"description":"Queued; `tutorial.completed` follows","content":{"application/json":{"example":{"id":"…","status":"queued","estimatedCostUsd":0.1}}}}}}},"/spend":{"get":{"tags":["Spend"],"summary":"This month's spend and ceiling","responses":{"200":{"description":"Totals, in credits","content":{"application/json":{"example":{"monthKey":"2026-09","spentThisMonthUsd":12.4,"ceilingUsd":50}}}}}}},"/spend/breakdown":{"get":{"tags":["Spend"],"summary":"By kind and six-month trend","responses":{"200":{"description":"Breakdown"}}}},"/spend/by-provider":{"get":{"tags":["Spend"],"summary":"By provider, this month","responses":{"200":{"description":"Breakdown"}}}},"/spend/by-story":{"get":{"tags":["Spend"],"summary":"By story, all time","responses":{"200":{"description":"Breakdown"}}}},"/webhooks":{"get":{"tags":["Webhooks"],"summary":"List endpoints","responses":{"200":{"description":"Endpoints and the event names"}}},"post":{"tags":["Webhooks"],"summary":"Add an endpoint","description":"The signing secret is returned once in this response and never again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["clip.completed","generation.failed","character.ready","keyframe.ready","shot.animated","animatic.ready","film.ready","short.ready","tutorial.completed","*"]}},"active":{"type":"boolean"}},"required":["url","events"],"additionalProperties":false}}}},"responses":{"201":{"description":"The endpoint with its secret"}}}},"/webhooks/{id}":{"patch":{"tags":["Webhooks"],"summary":"Edit an endpoint","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Endpoint id"}],"responses":{"200":{"description":"The endpoint"}}},"delete":{"tags":["Webhooks"],"summary":"Remove an endpoint","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Endpoint id"}],"responses":{"200":{"description":"Removed"}}}},"/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"summary":"Send a test.ping","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Endpoint id"}],"responses":{"202":{"description":"Queued"}}}},"/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"Recent deliveries","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Endpoint id"}],"responses":{"200":{"description":"Up to 50 deliveries, newest first"}}}},"/webhooks/deliveries/{id}/redeliver":{"post":{"tags":["Webhooks"],"summary":"Deliver again","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Delivery id"}],"responses":{"202":{"description":"Queued"}}}},"/providers":{"get":{"tags":["Reference"],"summary":"Providers, what each charges you, and 30-day health","responses":{"200":{"description":"Catalog"}}}},"/voices":{"get":{"tags":["Reference"],"summary":"Narration voices with preview clips","description":"The organization's own cloned voices come first (`own: true`), then the library. `cloning.available` says whether a new voice can be cloned on the account behind the studio.","responses":{"200":{"description":"Voices"}}},"post":{"tags":["Reference"],"summary":"Clone a voice from a recording","description":"One to three recordings of the same person (MP3, M4A, WAV, WebM or OGG, up to 10 MB each; one to three minutes of clear speech works best). The speaker's consent is required. The voice id returned is what characters and tutorial scripts take as `voiceId`.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Up to 60 characters."},"consent":{"type":"string","enum":["true"],"description":"Confirms this is your own voice or that the speaker agreed."},"samples":{"type":"string","format":"binary","description":"A recording."}},"required":["name","consent","samples"]}}}},"responses":{"201":{"description":"The cloned voice"}}}},"/voices/{id}":{"delete":{"tags":["Reference"],"summary":"Remove a cloned voice","description":"Characters and tutorial scripts that used it go back to the default voice.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Voice id"}],"responses":{"200":{"description":"Removed"}}}},"/org/current":{"get":{"tags":["Reference"],"summary":"The organization this key belongs to, and its permissions","responses":{"200":{"description":"Organization"}}}},"/notifications":{"get":{"tags":["Reference"],"summary":"In-app notifications","responses":{"200":{"description":"Notifications and the unread count"}}}}},"x-webhooks":{"description":"Each delivery is a POST with a JSON body `{ id, event, createdAt, organizationId, data }`. Verify it with the endpoint's secret: compute HMAC-SHA256 over `<x-animatra-timestamp>.<raw body>` and compare to the `x-animatra-signature` header (`sha256=<hex>`). Deliveries retry five times with backoff; a 2xx stops them.","events":["clip.completed","generation.failed","character.ready","keyframe.ready","shot.animated","animatic.ready","film.ready","short.ready","tutorial.completed"]}}