{"openapi":"3.1.0","info":{"title":"VIDEO AI ME API","version":"1.0.0","description":"Programmatic access to VIDEO AI ME skills: make video ads, publish them to connected social accounts, and check run status. Authenticate every skill and run endpoint with an API key (Authorization: Bearer va_live_...). Keys are managed at /api/v1/keys by a logged-in Pro or Premium user, carry a set of scopes chosen at creation (read, skills:run, flows:write, publish) and an optional expiration date. Every operation names its required scope in x-required-scope; calls with a key missing that scope get 403 insufficient_scope. Costs are quoted in credits; 1 credit = 1 cent."},"servers":[{"url":"https://videoai.me"}],"security":[{"apiKey":[]},{"oauth":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"API key in the va_live_... format, created at /api/v1/keys. Requires an active Pro or Premium subscription. Keys carry scopes: read (listings and run status), skills:run (makers, project edits, uploads; spends credits), flows:write (create and edit saved flows), publish (publish, schedule and manage ads). A key may also carry an expiration date; expired keys get 401 key_expired."},"oauth":{"type":"oauth2","description":"OAuth 2.1 access token, minted after the user clicks Authorize when adding this server in an MCP client. Consent is account wide, so a token carries every scope (read, skills:run, flows:write, publish) and has no per-key budget cap; the tier budget and the spend guard still apply. A token has no api_keys row behind it, so the per-key webhook endpoints and the Idempotency-Key header need an API key instead.","flows":{"authorizationCode":{"authorizationUrl":"https://nfveejwqdwexfyxrfwin.supabase.co/auth/v1/authorize","tokenUrl":"https://nfveejwqdwexfyxrfwin.supabase.co/auth/v1/token","scopes":{"read":"List and inspect templates, assets, projects, flows and runs.","skills:run":"Run maker skills and flows, edit projects and assets. Spends credits.","flows:write":"Create and edit saved flows.","publish":"Publish or schedule to connected accounts and manage ads."}}}}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Replay protection for a paid run. Send the same key to retry a request that timed out: the first call is executed and its response stored, and every retry returns that exact response with an Idempotent-Replay: true header instead of starting a second run. Reusing a key with a different body answers 409 idempotency_key_reused, and retrying while the original call is still running answers 409 idempotency_key_in_progress with a Retry-After header. A run that only answered needs_confirmation charges nothing and frees its key, so the confirmed call can reuse it. Keys are kept for 24 hours. Requests that fail validation or the budget check never consume a key.","schema":{"type":"string","maxLength":255,"examples":["9f1c2b7e-5a41-4f0d-9c3e-2b6a8d4e7f10"]}}},"schemas":{"EngineResult":{"type":"object","required":["skillId","status","message","files","cost","data"],"description":"The result envelope. Identical across REST, MCP and the chat, so a field added here appears on all three.","properties":{"skillId":{"type":"string"},"status":{"type":"string","enum":["completed","running","awaiting_approval","needs_confirmation","failed","cancelled"],"description":"The closed status vocabulary. A skill own word (started, pending, draft_ready, ...) is kept under data.status."},"message":{"type":"string","description":"One sentence, safe to show a user."},"runId":{"type":"string","description":"Poll handle. May belong to either run id space; checkUrl says which."},"checkUrl":{"type":"string","format":"uri","description":"Where to poll this run: /api/v1/runs/{id} for a skill run, /api/v1/flow-runs/{id} for a flow run."},"files":{"type":"array","description":"Everything the run produced, already resolved to fetchable URLs.","items":{"type":"object","required":["kind","url"],"properties":{"kind":{"type":"string","enum":["video","image","thumbnail","audio","srt"]},"url":{"type":"string","format":"uri"},"mime":{"type":"string"},"label":{"type":"string"},"aspect":{"type":"string","description":"Ratio label such as \"9:16\"."},"prompt":{"type":"string"}}}},"cost":{"type":"object","required":["estimatedCents","breakdown"],"description":"Credits, where 1 credit = 1 cent.","properties":{"estimatedCents":{"type":"integer"},"chargedCents":{"type":"integer","description":"What was really charged. Only known once the run has finished."},"breakdown":{"type":"array","items":{"type":"object","required":["label","cents"],"properties":{"label":{"type":"string"},"cents":{"type":"integer"}}}},"voiceMinutesUsed":{"type":"number","description":"Drawn from the monthly voice-minutes cap, a separate allowance."}}},"next":{"type":"object","required":["action","hint"],"description":"What the caller does next, so the instruction never has to be inferred.","properties":{"action":{"type":"string","enum":["confirm","approve_storyboard","poll","none"]},"skillId":{"type":"string","description":"The skill that performs the action."},"hint":{"type":"string"}}},"error":{"type":"object","required":["code","message","httpStatus","retryable"],"description":"Present only on a failure. REST also answers with the error body and status.","properties":{"code":{"type":"string"},"message":{"type":"string"},"httpStatus":{"type":"integer"},"retryable":{"type":"boolean","description":"Whether an identical call could succeed later untouched."},"issues":{"type":"array"}}},"data":{"type":"object","description":"The skill own payload, verbatim: postId, voices, mergeJobId, and everything else it returned.","additionalProperties":true}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"issues":{"type":"array","description":"Validation issues, present on 400 responses."}}}}},"WebhookConfig":{"type":"object","required":["url","secret"],"properties":{"url":{"type":["string","null"],"format":"uri"},"secret":{"type":["string","null"],"description":"The whsec_... signing secret. Plaintext only in the PUT response that created or regenerated it; masked everywhere else."},"events":{"type":"array","items":{"type":"string","enum":["run.completed","run.failed","run.cancelled","run.awaiting_approval","run.awaiting_confirmation"]}},"disabled":{"type":"boolean","description":"True when deliveries were auto-disabled after repeated failures."},"failureCount":{"type":"integer"},"message":{"type":"string"}}},"WebhookEvent":{"type":"object","description":"The JSON body POSTed to a registered webhook URL when a run changes state. Every delivery is signed: the X-VideoAIMe-Signature header carries \"t=<unix seconds>,v1=<hex hmac-sha256(secret, \\\"<t>.<raw body>\\\")>\", where secret is the key's whsec_... signing secret. Verify against the raw body before parsing; reject timestamps older than 5 minutes. Deliveries retry with backoff for up to 6 attempts until the receiver responds 2xx.","required":["id","type","createdAt","data"],"properties":{"id":{"type":"string","format":"uuid","description":"Stable across retries of one event."},"type":{"type":"string","enum":["run.completed","run.failed","run.cancelled","run.awaiting_approval","run.awaiting_confirmation"]},"createdAt":{"type":"string","format":"date-time"},"data":{"type":"object","required":["runId","skillId","status","checkUrl"],"properties":{"runId":{"type":"string","format":"uuid"},"skillId":{"type":"string"},"status":{"type":"string"},"estimatedCents":{"type":"integer"},"chargedCents":{"type":"integer"},"deliverable":{"type":"object","description":"Files, metadata, cost and timing; present on run.completed."},"error":{"type":"string"},"checkUrl":{"type":"string","format":"uri"}}}}},"FlowValue":{"description":"One step input value: a literal; a dot-path reference into an earlier step's result (asynchronous steps complete with { status, deliverable }, so references look like \"deliverable.files.0.url\"; a \"*\" segment maps over a list, \"deliverable.files.*.url\"); a reference to a flow input; or a prompt template mixing text with {{input.key}} and {{step-id.path}} expressions.","oneOf":[{"type":"object","required":["literal"],"properties":{"literal":{}},"additionalProperties":false},{"type":"object","required":["fromStep","path"],"properties":{"fromStep":{"type":"string","minLength":1},"path":{"type":"string","minLength":1}},"additionalProperties":false},{"type":"object","required":["fromInput"],"properties":{"fromInput":{"type":"string","minLength":1,"description":"A key from definition.inputs."}},"additionalProperties":false},{"type":"object","required":["template"],"properties":{"template":{"type":"string","maxLength":20000,"description":"Text with {{input.key}} and {{step-id.path}} expressions."}},"additionalProperties":false}]},"FlowInput":{"type":"object","description":"One run-time parameter of a flow: what a run asks the caller for.","required":["key","label","type"],"properties":{"key":{"type":"string","description":"Slug used in { fromInput } values and {{input.key}} templates."},"label":{"type":"string"},"type":{"type":"string","enum":["text","textarea","number","select","image","video","audio","boolean"]},"required":{"type":"boolean"},"default":{},"description":{"type":"string"},"options":{"type":"array","description":"Choices for select inputs; the value is what the flow receives.","items":{"type":"object","required":["value","label"],"properties":{"value":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"}}}}}},"FlowOutput":{"type":"object","description":"One named result of a flow, read out of a step's completed result.","required":["key","fromStep","path"],"properties":{"key":{"type":"string"},"fromStep":{"type":"string"},"path":{"type":"string"},"label":{"type":"string"}}},"FlowStep":{"type":"object","required":["id","skillId","input"],"properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","description":"Unique slug within the flow."},"skillId":{"type":"string","description":"A skill id from GET /api/v1/skills."},"input":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FlowValue"}},"dependsOn":{"type":"array","items":{"type":"string"},"description":"Earlier step ids this step waits on. References in input imply dependencies on their own."},"gate":{"type":"boolean","description":"Pause the run for a human approval before this step starts (release it with POST /api/v1/flow-runs/{runId}/approve)."},"label":{"type":"string","description":"Canvas title; never read by the executor."}}},"FlowEstimate":{"type":"object","required":["totalKnownCents","unknownSteps"],"properties":{"totalKnownCents":{"type":"integer","description":"Credits the priceable steps add up to; 1 credit = 1 cent."},"unknownSteps":{"type":"array","items":{"type":"string"},"description":"Step ids whose cost cannot be known before the run resolves its inputs."},"perStep":{"type":"object","description":"Estimated credits per step id; null when the step is priced at runtime.","additionalProperties":{"type":["integer","null"]}}}},"FlowDefinition":{"type":"object","required":["name","steps"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"description":{"type":"string","maxLength":500},"inputs":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/FlowInput"},"description":"Run-time parameters; values arrive in the run body as inputs."},"outputs":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/FlowOutput"}},"steps":{"type":"array","minItems":1,"maxItems":25,"items":{"$ref":"#/components/schemas/FlowStep"},"description":"A DAG: every dependency and reference must resolve to another step without cycles."}}},"FlowRunPlan":{"type":"object","description":"What this run does with each step: execute (runs now), reused (copied from reuseFromRunId at no charge), skipped (outside the targetStepIds closure).","required":["execute","reused","skipped"],"properties":{"execute":{"type":"array","items":{"type":"string"}},"reused":{"type":"array","items":{"type":"string"}},"skipped":{"type":"array","items":{"type":"string"}}}},"FlowRunReport":{"type":"object","description":"The one poll shape for a flow run: id-keyed step states, the deliverables of completed maker steps, the raw results of completed synchronous steps, and the plan bookkeeping.","required":["flowRunId","status","steps"],"properties":{"flowRunId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["running","awaiting_confirmation","completed","failed","cancelled"]},"totalEstimatedCents":{"type":"integer"},"chargedCents":{"type":"integer"},"currentSteps":{"type":"array","items":{"type":"string"}},"awaitingApproval":{"type":"array","items":{"type":"string"},"description":"Steps paused at a gate; release them with POST /api/v1/flow-runs/{runId}/approve."},"steps":{"type":"object","additionalProperties":{"type":"object","properties":{"status":{"type":"string","enum":["pending","running","awaiting_approval","completed","failed","skipped"]},"skillRunId":{"type":"string","format":"uuid"},"error":{"type":"string"},"message":{"type":"string","description":"Guidance for paused steps (how to approve them)."},"chargedCents":{"type":"integer"},"estimatedCents":{"type":"integer"},"reusedFromRunId":{"type":"string","format":"uuid","description":"Set when the result came from an earlier run at no charge."},"gated":{"type":"boolean","description":"True while the step waits at a gate for an approval."}}}},"deliverables":{"type":"object","description":"Step id to Deliverable manifest for completed maker steps.","additionalProperties":{"type":"object"}},"results":{"type":"object","description":"Step id to raw result (text, JSON) of completed synchronous steps.","additionalProperties":{}},"outputs":{"type":"object","description":"The flow's declared outputs resolved from completed steps, keyed by output key.","additionalProperties":{}},"inputs":{"type":"object","description":"The flow inputs this run executes with.","additionalProperties":{}},"targetStepIds":{"type":["array","null"],"items":{"type":"string"},"description":"The steps this run was asked to produce; null = every step."},"reusedFromRunId":{"type":["string","null"],"format":"uuid","description":"The run whose completed steps seeded this one."},"error":{"type":"string"}}},"list_templates_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists every video template the platform can run: id, what it makes, supported durations and aspect ratios, and the honest credit cost range (1 credit = 1 cent). Free and read-only; call it before proposing a maker skill so cost quotes are accurate."},"list_models_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists every generation model the platform can run: id, kind (video or image), display name, supported resolutions with the honest cost basis (per-second credits for video, per-image credits for image, 1 credit = 1 cent), clip length windows, and capability flags (start-frame animation, text-to-video, baked-in narration, end-frame interpolation). Free and read-only; call it before make_image_to_video or any model choice so model ids and cost quotes are accurate."},"list_actors_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the user's saved actors with their looks (id, style, status, aspect ratio). Only looks with status \"completed\" can be animated; anything else is still generating or failed. Free and read-only. Call it before make_talking_head: that skill needs a completed look id from here."},"list_voices_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the available voices: the platform voice library plus the user's own cloned voices. Free and read-only; the voiceId values work directly with make_voiceover."},"get_brand_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Reads the user's saved brand memory: business facts, target customer, tone of voice, banned words, preferred actors and voices, platform handles and notes. Free and read-only; check it before writing any script or caption so the output matches the brand."},"update_brand_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"businessName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"websiteUrl":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"products":{"maxItems":50,"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"targetCustomer":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"toneOfVoice":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"languages":{"maxItems":20,"type":"array","items":{"type":"string","maxLength":40}},"bannedWords":{"maxItems":200,"type":"array","items":{"type":"string","maxLength":80}},"preferredActorIds":{"maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"preferredVoiceIds":{"maxItems":50,"type":"array","items":{"type":"string","maxLength":80}},"platformHandles":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string","maxLength":120}},"goals":{"maxItems":20,"type":"array","items":{"type":"string","maxLength":300}},"notes":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]}},"description":"Saves brand facts the user shares (business name, products, target customer, tone of voice, banned words, preferred actors/voices, platform handles, notes) so future runs stay on brand. Only the fields you pass change. Free; confirm with the user before overwriting something they saved earlier."},"list_platform_accounts_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the social accounts the user has connected for publishing: platform, accountId, username and connection status. Free and read-only. Call it before publish_video or schedule_post: those skills need accountId values from here, and accounts marked needs_reauth cannot receive posts."},"analyze_website_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","minLength":4,"maxLength":500,"description":"The website to analyze, e.g. https://example.com (a bare domain also works)."}},"required":["url"],"description":"Reads the user's website and drafts their brand memory (business name, products, target customer, tone of voice, languages) plus three starter video pitches. Use it when brand memory is empty or thin and the user has a website. Free and read-only: it returns a DRAFT and saves nothing. Show the draft to the user as an editable summary, let them correct it, and save only what they confirm with update_brand. The three pitches are conversation starters, not commitments."},"make_ugc_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"productName":{"type":"string","minLength":1,"maxLength":80},"script":{"type":"string","minLength":1,"maxLength":500},"actorDescription":{"type":"string","maxLength":90},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9"]},"durationSeconds":{"default":30,"type":"integer","minimum":10,"maximum":120},"hook":{"type":"string","maxLength":140},"voice":{"default":"female","type":"string","enum":["male","female"]},"setting":{"default":"bedroom","type":"string","enum":["bedroom","kitchen","outdoors","desk"]},"creatorPhotoUrl":{"type":"string","maxLength":2000},"productPhotoUrl":{"type":"string","maxLength":2000},"avoid":{"type":"string","maxLength":200},"confirmed":{"type":"boolean"}},"required":["productName","script"],"description":"Creates a talking-head UGC ad: one creator filming themselves at home, speaking a script written from your brief. creatorPhotoUrl and productPhotoUrl accept an https URL or the assetId of a saved image asset (list_assets). Spends real video credits (1 credit = 1 cent), so always tell the user the estimated cost before starting, and only pass confirmed: true after they agree. The run pauses at a storyboard approval step before the expensive clip phase; check progress with check_run and approve with approve_storyboard (or the approval URL). A finished ad typically takes 6 to 25 minutes."},"make_product_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"productName":{"type":"string","minLength":1,"maxLength":80},"brief":{"type":"string","minLength":1,"maxLength":600},"productImages":{"default":[],"maxItems":4,"type":"array","items":{"type":"string","maxLength":2000}},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9"]},"durationSeconds":{"default":15,"type":"integer","minimum":5,"maximum":20},"style":{"default":"studio-minimal","type":"string","enum":["studio-minimal","natural-textures","lifestyle-context"]},"avoid":{"type":"string","maxLength":200},"confirmed":{"type":"boolean"}},"required":["productName","brief"],"description":"Creates a polished product ad from a short brief: storyboard, key visuals, then one continuous video with sound. The first product image is used as the visual reference; productImages entries accept an https URL or the assetId of a saved image asset (list_assets). Spends real video credits (1 credit = 1 cent), so always tell the user the estimated cost before starting, and only pass confirmed: true after they agree. The run pauses at a storyboard approval step before video credits are spent; check progress with check_run and approve with approve_storyboard (or the approval URL). A finished ad typically takes 6 to 25 minutes."},"make_explainer_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"topic":{"type":"string","minLength":1,"maxLength":80},"brief":{"type":"string","minLength":1,"maxLength":600},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9"]},"durationSeconds":{"default":30,"type":"integer","minimum":10,"maximum":40},"narratorVoice":{"default":"female","type":"string","enum":["male","female"]},"productPhotoUrl":{"type":"string","format":"uri"},"avoid":{"type":"string","maxLength":200},"confirmed":{"type":"boolean"}},"required":["topic","brief"],"description":"Creates a paper-collage explainer video with baked-in narration from a topic and a short brief (a narrated scene-by-scene script, one collage board per scene, then animated clips cut together). Spends real video credits (1 credit = 1 cent), so always tell the user the estimated cost before starting, and only pass confirmed: true after they agree. The run pauses at a board approval step before video credits are spent; check progress with check_run and approve with approve_storyboard (or the approval URL). A finished explainer typically takes 6 to 25 minutes."},"make_talking_head_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"actorLookId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"script":{"type":"string","maxLength":600},"voiceId":{"type":"string","minLength":1},"durationSeconds":{"default":5,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"draftRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"confirmed":{"type":"boolean"}},"required":["actorLookId"],"description":"Animates one of the user's saved actor looks into a talking-head video clip (5 or 10 seconds). Know this before you spend anything: the filmed clip is SILENT. The animation carries no audio track, so no call to this skill ever returns a clip that speaks; sound is laid over the finished clip afterwards by add_voiceover, which is free. Best practice is draft first, film second: pass the script plus a voiceId from list_voices to get a cheap draft (the actor look image plus the script spoken in that voice, paid from the monthly voice minutes cap, no video credits) - ALWAYS show the draft to the user and get their approval of the look, voice and script before filming. Once approved, call again with draftRunId and confirmed: true to film the same validated script. The film stage ALWAYS pauses for confirmation first, whatever the amount, and its reply states both the cost and the fact that the clip comes back mute; what those credits (1 credit = 1 cent) buy is a silent 5 or 10 second animation of the look, nothing more. Once check_run reports the film complete, call add_voiceover with runId set to the film run and audioRunId set to the draft runId to make it speak: the draft audio is already paid for, so no further voice minutes are spent. WITHOUT a voiceId the clip films directly with no draft and is silent in exactly the same way; there is no paid-for voice waiting, so run make_voiceover first and pass the runId it returns to add_voiceover. Call list_actors first to pick a completed look id, and poll everything with check_run."},"make_image_to_video_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"imagePrompt":{"type":"string","minLength":1,"maxLength":4000},"imageUrl":{"type":"string","format":"uri"},"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"videoPrompt":{"type":"string","minLength":1,"maxLength":4000},"model":{"default":"seedance25","type":"string","minLength":1},"durationSeconds":{"default":8,"type":"integer","minimum":1,"maximum":30},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9"]},"resolution":{"default":"720p","type":"string","enum":["480p","720p","768p","1080p","2k"]},"numImages":{"default":1,"type":"integer","minimum":1,"maximum":4},"stage1RunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"confirmed":{"type":"boolean"}},"required":["videoPrompt"],"description":"Makes a video the image-first way: great videos start from a great first frame, so generate the image cheap, get the user's approval on it, then animate. Stage 1 (imagePrompt): generates start-frame candidates from the monthly image quota, no video credits spent, and returns them with stage2EstimateCents; ALWAYS show the image to the user and get their approval of the frame before the video stage. Stage 2 (imageUrl or assetId): animates the approved frame; this spends real video credits (1 credit = 1 cent), so quote the cost and pass confirmed: true plus the stage1RunId only after the user agrees. Poll the video with check_run. Defaults to the strongest start-frame model by capability (start plus end frame support and the longest clips); pass model to pick another start-frame capable model."},"make_voiceover_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":5000},"voiceId":{"type":"string","minLength":1},"clipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["text","voiceId"],"description":"Turns text into a spoken audio track with one of the user's voices, and returns the audio URL. Call list_voices first for a voiceId (platform library or a voice cloned with clone_voice). The audio stands on its own: it is attached to nothing until you pass the runId this returns to add_voiceover as audioRunId, which lays the track over a finished video and returns the combined clip. That is what pairs with make_talking_head, whose filmed clips are silent. It spends no credits: it draws from the monthly voice minutes cap instead (roughly one minute per 750 characters), a separate allowance from the credit budget, so a user with credits left can still run out of voice minutes."},"add_voiceover_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"videoUrl":{"type":"string","format":"uri"},"clipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"audioUrl":{"type":"string","format":"uri"},"audioRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"description":"Lays a voice-over (or any audio track) over a finished video and returns the combined video. This is the ONLY way to attach audio to a video: the typical chain is make_voiceover then add_voiceover, or make_talking_head without a voiceId (which films a silent clip) then make_voiceover then add_voiceover. Pass exactly one video source (videoUrl, clipId, or the runId of a completed run, including a merge_videos run) and exactly one audio source (audioUrl, or audioRunId: the runId make_voiceover returned). The audio REPLACES the video's existing audio track rather than mixing with it, and the result stops when the shorter of the two ends, so match the script length to the clip. Included at no credit cost, and it spends no voice minutes of its own. Poll the result with check_run using the runId it returns."},"add_captions_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"clipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"description":"Transcribes a completed video and burns word-level captions into the picture, returning the captioned video URL. Free. Captions are opt-in: ask the user before adding them. Pass the clipId of a finished clip, or the runId of a completed maker skill run."},"merge_videos_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"videoUrls":{"minItems":2,"maxItems":10,"type":"array","items":{"type":"string","format":"uri"}},"runIds":{"minItems":2,"maxItems":10,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"clipIds":{"minItems":2,"maxItems":10,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"confirmed":{"type":"boolean"}},"description":"Stitches 2 to 10 finished videos into one file, in the exact order given. It concatenates the video files only: it does NOT add or replace audio. Each source clip keeps its own audio, and a clip that was silent stays silent. To lay a voice-over over the result, merge first and then call add_voiceover with the merged runId. Audio inputs are refused outright: an .mp3, .wav, .m4a, .aac, .ogg or .flac in videoUrls, or a runId whose output is an audio track, is rejected before anything is submitted, so never try to add sound by putting the audio file in the list. Included at no credit cost. Order matters: ALWAYS present the ordered list of clips to the user and confirm it before merging. Pass direct videoUrls, runIds of completed maker skill runs, or clipIds of finished clips, then poll the merged result with check_run: a merge that has not finished within 10 minutes is reported as failed, so polling never runs forever."},"create_actor_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"originalPath":{"type":"string","minLength":1},"processedPath":{"type":"string","minLength":1},"skipLooks":{"default":false,"type":"boolean"}},"required":["name","originalPath","processedPath"],"description":"Creates a reusable actor from a photo in the user's images storage, and launches its 4 automatic looks. To upload a photo you were given, POST /api/v1/uploads (Bearer key) with { kind: \"image\", filename }, PUT the bytes to the returned uploadUrl, then pass the returned storagePath as BOTH originalPath and processedPath (processedPath is only a separately cropped copy when you have one). A storagePath of an image made by generate_images works the same way. This draws one slot from the monthly custom actor cap plus 4 images from the monthly image quota, and the looks take a few minutes to finish generating: tell the user before starting, then check list_actors for the finished looks."},"clone_voice_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"consent":{"type":"boolean","const":true},"storagePath":{"type":"string","minLength":1},"name":{"type":"string","minLength":2,"maxLength":50},"description":{"type":"string","maxLength":500},"contentType":{"type":"string"}},"required":["consent","storagePath","name"],"description":"Clones a custom voice from an audio sample in the user's voice sample storage. To upload a sample you were given, POST /api/v1/uploads (Bearer key) with { kind: \"voice\", filename }, PUT the bytes to the returned uploadUrl, then pass the returned storagePath here. REQUIRED: confirm with the user that the voice is their own or that they have the speaker's explicit consent, and only then pass consent: true - never clone a voice without that confirmation. Draws one slot from the monthly custom voice cap. The cloned voiceId works with make_voiceover afterwards."},"approve_storyboard_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"approve":{"type":"boolean","const":true}},"required":["runId","approve"],"description":"Approves the storyboard frames of a paused maker run (make_ugc_ad, make_product_ad, make_explainer) and starts the paid clip phase, exactly like approving in the app. Approving commits real video credits: ALWAYS show the user the frames (or the approval URL from check_run) and get their explicit approval first, then call with approve: true and the runId. Free to call; the clip spend was already quoted when the run started. Keep polling with check_run afterwards."},"publish_video_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"confirmPlatforms":{"type":"boolean","const":true},"targets":{"minItems":1,"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","minLength":1},"accountId":{"type":"string","minLength":1},"username":{"type":"string"}},"required":["platform","accountId"]}},"content":{"type":"string","maxLength":5000},"media":{"type":"object","properties":{"source":{"type":"string","enum":["merge_job","template_run","upload","url"]},"sourceId":{"type":"string"},"storagePath":{"type":"string"},"url":{"type":"string","maxLength":2000,"format":"uri"}},"required":["source"]},"publishNow":{"default":true,"type":"boolean"},"scheduledAt":{"type":"string"},"timezone":{"default":"UTC","type":"string"},"platformSettings":{"type":"object","properties":{"tiktok":{"type":"object","properties":{"draft":{"type":"boolean"},"privacyLevel":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"]},"allowComment":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"}}}}}},"required":["confirmPlatforms","targets","media"],"description":"Publishes or schedules a finished video to the user's connected social accounts. Publishing is an outward action on real accounts: ALWAYS confirm the exact platform list with the user first, and only then call with confirmPlatforms: true. Delivery is asynchronous and this skill NEVER confirms it: a status of \"pending\" means the publishing service accepted the post, and it can still fail on the platform seconds or minutes later, so never tell the user the video is live on the strength of this result. Read the real outcome with get_performance using the returned postId; a post that failed comes back there with the platform's own reason. A target the service refuses at submit time is returned in errors[] with that same wording and status is \"failed\". For TikTok, platformSettings.tiktok.draft: true sends the video to the Creator Inbox as a draft the user finishes posting from the TikTok app: it is the recommended route when a direct publish keeps getting refused, and the AI-content disclosure stays on either way. Costs no credits. Use check_run on a maker skill first if the video is still generating."},"schedule_post_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"confirmPlatforms":{"type":"boolean","const":true},"targets":{"minItems":1,"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","minLength":1},"accountId":{"type":"string","minLength":1},"username":{"type":"string"}},"required":["platform","accountId"]}},"content":{"type":"string","maxLength":5000},"media":{"type":"object","properties":{"source":{"type":"string","enum":["merge_job","template_run","upload"]},"sourceId":{"type":"string"},"storagePath":{"type":"string"}},"required":["source"]},"useQueue":{"default":false,"type":"boolean"},"scheduledAt":{"type":"string"},"timezone":{"default":"UTC","type":"string"},"platformSettings":{"type":"object","properties":{"tiktok":{"type":"object","properties":{"draft":{"type":"boolean"},"privacyLevel":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"]},"allowComment":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"}}}}}},"required":["confirmPlatforms","targets","media"],"description":"Schedules a finished video onto the user's posting queue (useQueue: true takes the next free slot of their weekly schedule) or at an explicit time (scheduledAt). Posting is an outward action on real accounts: ALWAYS confirm the exact platform list and the timing with the user first, and only then call with confirmPlatforms: true. A status of \"scheduled\" means the slot was accepted, nothing more: the post is delivered later and can still fail on the platform, so it is never proof that anything went live. After the scheduled time, read the real outcome with get_performance using the returned postId, which reports a failure with the platform's own reason. A target refused at submit time comes back in errors[] with that wording. For TikTok, platformSettings.tiktok.draft: true schedules the video into the Creator Inbox as a draft for the user to finish posting. Costs no credits."},"get_performance_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"limit":{"default":50,"type":"integer","minimum":1,"maximum":100},"postId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"description":"Reads how the user's posts actually did: per-platform delivery status, metrics (views, likes, comments, shares, clicks) and totals per post. This is also the way to verify a publish: pass the postId that publish_video or schedule_post returned and it answers whether that post really went live, with deliveryConfirmed true only when every platform confirmed it. A post that failed comes back with outcome \"failed\" and the platform's own error text, so quote that to the user instead of assuming success. Read-only, free and safe to poll every minute or two; engagement metrics refresh with the daily analytics sync."},"list_ad_accounts_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the ad accounts the user has connected for paid campaigns: each connected account (accountId, platform, name, import state) plus the platform ad accounts under it (adAccountId, name, currency). Free and read-only. Call it first: launch_ad, boost_post, get_ad_account_timeline and get_ad_account_finance all need an accountId and adAccountId pair from here. Only works on accounts where ads access is enabled."},"list_ad_campaigns_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string"},"adAccountId":{"type":"string"},"platform":{"type":"string"},"status":{"type":"string"},"fromDate":{"type":"string"},"toDate":{"type":"string"},"sort":{"type":"string","enum":["newest","oldest","spend_desc","spend_asc"]},"hasDelivery":{"type":"boolean"},"minSpend":{"type":"number","minimum":0},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"limit":{"type":"integer","minimum":1,"maximum":100}},"description":"Lists the paid campaigns on the user's connected ad accounts with status, budget, currency and rolled-up metrics (spend, impressions, reach, clicks, CTR, CPC, CPM, conversions, ROAS) for the date range. Free. Use it to find the campaignId that get_ad_campaign, get_ad_performance, set_ad_status, update_ad_budget and the other management skills need. Filter by account, platform or status, sort by spend, and page through large accounts."},"get_ad_campaign_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"fromDate":{"type":"string"},"toDate":{"type":"string"},"dailyLevel":{"type":"string","enum":["campaign","adset","ad"]}},"required":["campaignId"],"description":"Returns one campaign with its ad sets and ads: status, review and serving state, budget and budget level (campaign or ad set), each ad creative (headline, body, link, call to action, thumbnail), metrics per node, and optionally a daily series. Free. Read this before changing budgets or creatives so you know which ad set holds the budget and which ad is which."},"get_ad_performance_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adId":{"type":"string"},"fromDate":{"type":"string"},"toDate":{"type":"string"},"breakdowns":{"maxItems":4,"type":"array","items":{"type":"string","enum":["age","gender","country","region","publisher_platform","platform_position","device_platform","impression_device","job_title","job_function","seniority","industry","company","company_size"]}}},"required":["campaignId"],"description":"Reads how a paid campaign (or one ad inside it, with adId) is doing on the user's ad account: spend, impressions, reach, clicks, CTR, CPC, CPM, conversions, cost per conversion, ROAS, video plays and thruplays, a daily timeline, and optional breakdowns by age, gender, country, region, network (publisher_platform), placement (platform_position) or device (device_platform); LinkedIn adds job title, function, seniority, industry, company and company size. Free. Get the campaignId from list_ad_campaigns. Money figures are in the ad account's currency and are the user's own ad spend."},"get_ad_account_timeline_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string"},"fromDate":{"type":"string"},"toDate":{"type":"string"}},"required":["accountId"],"description":"Daily totals across every campaign on a connected ad account: spend, impressions, reach, clicks, CTR, CPC, CPM, conversions and ROAS per day. Free. The honest series for \"how is my ad spend trending\"; use get_ad_performance for one campaign. Get accountId from list_ad_accounts."},"get_ad_account_finance_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1}},"required":["accountId","adAccountId"],"description":"Reads the money side of one platform ad account: prepaid balance, lifetime amount spent, spend cap and funding source, plus the EU DSA defaults (beneficiary and payor) used for European ads. Free. Explains why an ad stopped (cap reached, no funding) without opening the platform. Some platforms do not report finances; then finance is null."},"get_ad_activity_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string"},"campaignId":{"type":"string"},"limit":{"type":"integer","minimum":1,"maximum":200}},"description":"One time-ordered log of what happened on an ad account: the platform's own change history (any tool, any person) merged with the actions taken from this workspace and the surface they came from. Free. Use it to answer 'who paused this' or 'when did the budget change'."},"list_ad_pixels_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1}},"required":["accountId","adAccountId"],"description":"Lists the conversion sources a launch can optimize for on a platform ad account: conversion actions (pixels and events) and lead forms. Free. Call it before launch_ad with a conversions or lead_conversion goal so you can pass a real pixelId or leadGenFormId instead of guessing."},"search_ad_targeting_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string"},"dimension":{"type":"string","enum":["geo","interest","behavior","income"]},"q":{"type":"string","minLength":1,"maxLength":120},"geoType":{"type":"string"},"countryCode":{"type":"string","minLength":2,"maxLength":2},"limit":{"type":"integer","minimum":1,"maximum":50}},"required":["accountId","dimension","q"],"description":"Resolves a human phrase into the platform's targeting ids: cities and regions (dimension geo), interests, behaviors or income tiers, each with an audience size where the platform reports one. Free. Use the returned ids in launch_ad interests, cities or regions; unavailable is true when the platform has no search for that dimension."},"estimate_ad_reach_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1},"spec":{"type":"object","properties":{"countries":{"maxItems":50,"type":"array","items":{"type":"string","minLength":2,"maxLength":2}},"ageMin":{"type":"integer","minimum":13,"maximum":100},"ageMax":{"type":"integer","minimum":13,"maximum":100},"gender":{"type":"string","enum":["all","male","female"]},"interests":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id"]}},"cities":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"radius":{"type":"number"},"distance_unit":{"type":"string","enum":["kilometer","mile"]}},"required":["key"]}},"regions":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"}},"required":["key"]}},"languages":{"maxItems":20,"type":"array","items":{"type":"string"}}}},"optimizationGoal":{"type":"string"}},"required":["accountId","adAccountId","spec"],"description":"Pre-flight audience size for a targeting spec (countries, ages, gender, interests, cities, regions) on a platform ad account, before any campaign exists. Free. Returns lower and upper bounds where the platform offers an estimate, or null where it does not (search and some video platforms)."},"preview_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string"},"adId":{"type":"string"},"accountId":{"type":"string"},"adAccountId":{"type":"string"},"creativeSpec":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"formats":{"maxItems":6,"type":"array","items":{"type":"string"}}},"description":"Renders how an ad looks per placement (mobile feed, desktop feed, story) as embeddable HTML snippets, either for an existing ad (campaignId + adId) or for a creative spec before anything exists (accountId + adAccountId + creativeSpec). Free. Previews are available on Meta; elsewhere unavailable is true."},"list_boostable_posts_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"platform":{"type":"string"},"limit":{"type":"integer","minimum":1,"maximum":200}},"description":"Lists posts published from this workspace that a connected ad account can promote with boost_post, with the platform post id, the caption and the media. Free. Boosting keeps the likes and comments the post already earned."},"launch_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"countries":{"maxItems":50,"type":"array","items":{"type":"string","minLength":2,"maxLength":2}},"ageMin":{"type":"integer","minimum":13,"maximum":100},"ageMax":{"type":"integer","minimum":13,"maximum":100},"gender":{"type":"string","enum":["all","male","female"]},"interests":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id"]}},"cities":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"radius":{"type":"number"},"distance_unit":{"type":"string","enum":["kilometer","mile"]}},"required":["key"]}},"regions":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"}},"required":["key"]}},"languages":{"maxItems":20,"type":"array","items":{"type":"string"}},"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":200},"goal":{"type":"string","minLength":1},"media":{"type":"object","properties":{"source":{"type":"string","enum":["merge_job","template_run","upload","url","image"]},"sourceId":{"type":"string"},"storagePath":{"type":"string"},"url":{"type":"string","maxLength":2000,"format":"uri"}},"required":["source"]},"headline":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":2000},"description":{"type":"string","maxLength":500},"linkUrl":{"type":"string","format":"uri"},"callToAction":{"type":"string","maxLength":60},"budgetAmount":{"type":"number","exclusiveMinimum":0},"budgetType":{"type":"string","enum":["daily","lifetime"]},"budgetLevel":{"type":"string","enum":["adset","campaign"]},"startDate":{"type":"string"},"endDate":{"type":"string"},"organizationId":{"type":"string"},"boardId":{"type":"string"},"pixelId":{"type":"string"},"customEventType":{"type":"string"},"urlTags":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"value":{"type":"string"}},"required":["key","value"]}},"placements":{"type":"object","properties":{"publisherPlatforms":{"type":"array","items":{"type":"string"}},"facebookPositions":{"type":"array","items":{"type":"string"}},"instagramPositions":{"type":"array","items":{"type":"string"}},"devicePlatforms":{"type":"array","items":{"type":"string"}}}},"dsaBeneficiary":{"type":"string"},"dsaPayor":{"type":"string"},"adSetId":{"type":"string"},"existingCampaignId":{"type":"string"},"validateOnly":{"type":"boolean"},"activate":{"default":false,"type":"boolean"},"confirmLaunch":{"type":"boolean","const":true}},"required":["accountId","adAccountId","name","goal","media","budgetAmount","confirmLaunch"],"description":"Turns a finished video (or an image on platforms with image ads) into a paid campaign on one of the user's connected ad accounts. Call list_ad_accounts first for the accountId and adAccountId. Targeting accepts countries, ages, gender, interests, cities and regions (ids from search_ad_targeting); conversions goals take a pixelId (list_ad_pixels); LinkedIn needs organizationId, Pinterest a boardId. On Meta the ad runs on Facebook and Instagram only unless placements.publisherPlatforms adds audience_network or messenger; Audience Network clicks rarely convert. The budget is the user's own ad spend, billed by the ad platform to their ad account, never platform credits. The campaign is created PAUSED unless activate is explicitly true, so nothing spends by surprise. Requires confirmLaunch: true: ALWAYS present the full campaign summary (platform, ad account, objective, budget, schedule, targeting, creative) and get the user's explicit approval before calling."},"boost_post_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1},"postId":{"type":"string","minLength":1},"platformPostId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":200},"goal":{"type":"string","minLength":1},"budgetAmount":{"type":"number","exclusiveMinimum":0},"budgetType":{"type":"string","enum":["daily","lifetime"]},"startDate":{"type":"string"},"endDate":{"type":"string"},"targeting":{"type":"object","properties":{"countries":{"maxItems":50,"type":"array","items":{"type":"string","minLength":2,"maxLength":2}},"ageMin":{"type":"integer","minimum":13,"maximum":100},"ageMax":{"type":"integer","minimum":13,"maximum":100},"gender":{"type":"string","enum":["all","male","female"]},"interests":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id"]}},"cities":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"radius":{"type":"number"},"distance_unit":{"type":"string","enum":["kilometer","mile"]}},"required":["key"]}},"regions":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"key":{"type":"string"}},"required":["key"]}},"languages":{"maxItems":20,"type":"array","items":{"type":"string"}}}},"linkUrl":{"type":"string","format":"uri"},"callToAction":{"type":"string","maxLength":60},"adSetId":{"type":"string"},"existingCampaignId":{"type":"string"},"dsaBeneficiary":{"type":"string"},"dsaPayor":{"type":"string"},"activate":{"default":false,"type":"boolean"},"confirmLaunch":{"type":"boolean","const":true}},"required":["accountId","adAccountId","name","goal","budgetAmount","confirmLaunch"],"description":"Promotes a post the user already published (list_boostable_posts, or any platformPostId) as a paid ad, keeping the likes and comments it earned. Takes the same budget, schedule and targeting as launch_ad; on Meta an adSetId attaches it to an existing ad set instead. The budget is the user's own ad spend, billed by the ad platform to their ad account, never platform credits. Created PAUSED unless activate is true. Requires confirmLaunch: true after the user approved the post, budget and audience."},"set_ad_status_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"level":{"type":"string","enum":["campaign","adset","ad"]},"campaignId":{"type":"string","minLength":1},"adSetId":{"type":"string"},"adId":{"type":"string"},"status":{"type":"string","enum":["active","paused"]},"confirm":{"type":"boolean"}},"required":["level","campaignId","status"],"description":"Pauses or resumes a whole campaign, one ad set, or one ad. Pausing needs no confirmation and stops spend at once. Resuming (status active) requires confirm: true because it restarts spend: tell the user what will start spending and get their yes first. The budget is the user's own ad spend, billed by the ad platform to their ad account, never platform credits. Get ids from list_ad_campaigns and get_ad_campaign."},"pause_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1}},"required":["campaignId"],"description":"Pauses an entire ad campaign so it stops spending the user's own ad budget; the paused status cascades through the campaign's ad sets and ads. Free and safe: pausing never deletes anything. Kept for compatibility: prefer set_ad_status, which also resumes and works per ad set or per ad."},"bulk_ad_campaign_status_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignIds":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1}},"status":{"type":"string","enum":["active","paused"]},"confirm":{"type":"boolean"}},"required":["campaignIds","status"],"description":"Pauses or resumes up to 50 campaigns in one call, with a per-campaign result so one refusal does not fail the batch. Resuming requires confirm: true after the user approved which campaigns start spending again. The budget is the user's own ad spend, billed by the ad platform to their ad account, never platform credits."},"update_ad_budget_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adSetId":{"type":"string"},"amount":{"type":"number","exclusiveMinimum":0},"type":{"type":"string","enum":["daily","lifetime"]},"confirm":{"type":"boolean"}},"required":["campaignId","amount"],"description":"Changes the daily or lifetime budget of a campaign, or of one ad set when the campaign holds budgets per ad set. Lowering applies at once. Raising returns needs_confirmation with the current and requested amounts until you call again with confirm: true after the user approved. The budget is the user's own ad spend, billed by the ad platform to their ad account, never platform credits."},"rename_ad_campaign_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adSetId":{"type":"string"},"adId":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":200}},"required":["campaignId","name"],"description":"Renames a campaign, or with adSetId or adId one ad set or ad inside it, on the ad platform. Free, no spend change. Names help agencies keep dozens of campaigns readable; the platform shows the new name everywhere."},"duplicate_ad_campaign_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"deepCopy":{"type":"boolean"},"renamePrefix":{"type":"string","maxLength":60},"renameSuffix":{"type":"string","maxLength":60},"startTime":{"type":"string"},"endTime":{"type":"string"}},"required":["campaignId"],"description":"Copies a campaign with its ad sets, ads, creatives and targeting. The copy is always created PAUSED so it spends nothing until the user resumes it with set_ad_status. Use it to test a new budget, audience or creative next to a winner without touching the original."},"duplicate_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adId":{"type":"string","minLength":1},"adSetId":{"type":"string"},"renamePrefix":{"type":"string","maxLength":60},"renameSuffix":{"type":"string","maxLength":60}},"required":["campaignId","adId"],"description":"Copies one ad inside its campaign (same ad set by default, or another ad set with adSetId). The copy is created PAUSED. Pair it with update_ad_creative to run a headline or call-to-action variant against the original."},"update_ad_creative_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adId":{"type":"string","minLength":1},"name":{"type":"string","maxLength":200},"headline":{"type":"string","maxLength":300},"body":{"type":"string","maxLength":2000},"description":{"type":"string","maxLength":500},"callToAction":{"type":"string","maxLength":60},"linkUrl":{"type":"string","format":"uri"},"imageUrl":{"type":"string","format":"uri"},"videoUrl":{"type":"string","format":"uri"},"headlines":{"maxItems":15,"type":"array","items":{"type":"string","minLength":1,"maxLength":90}},"descriptions":{"maxItems":4,"type":"array","items":{"type":"string","minLength":1,"maxLength":180}},"finalUrls":{"maxItems":5,"type":"array","items":{"type":"string","format":"uri"}}},"required":["campaignId","adId"],"description":"Edits an existing ad in place: name, headline, primary text, description, call to action, landing page, image or video URL, or the rotating headlines, descriptions and final URLs on search platforms. No spend change, but on Meta a creative edit makes a new creative and sends the ad back into review, so say so to the user. Read get_ad_campaign first to quote the current creative."},"archive_ad_campaign_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"confirmDelete":{"type":"boolean","const":true}},"required":["campaignId","confirmDelete"],"description":"Deletes a campaign on the ad platform, with every ad set and ad in it. Irreversible: the campaign cannot be resumed afterwards, though its spend history stays readable in reports. Requires confirmDelete: true; name the campaign and get the user's explicit yes before calling. To stop spend without losing the campaign, use set_ad_status paused instead."},"cancel_ad_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"campaignId":{"type":"string","minLength":1},"adId":{"type":"string","minLength":1},"confirmDelete":{"type":"boolean","const":true}},"required":["campaignId","adId","confirmDelete"],"description":"Cancels (deletes) one ad inside a campaign on the ad platform. Irreversible for that ad; the rest of the campaign keeps running. Requires confirmDelete: true after the user named the ad and agreed. To stop one ad without deleting it, use set_ad_status with level ad."},"set_ad_account_defaults_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accountId":{"type":"string","minLength":1},"adAccountId":{"type":"string","minLength":1},"dsaBeneficiary":{"type":"string","minLength":1,"maxLength":200},"dsaPayor":{"type":"string","maxLength":200}},"required":["accountId","adAccountId","dsaBeneficiary"],"description":"Saves the EU DSA beneficiary and payor once per platform ad account, so every later launch targeting Europe can omit them. Free. Read the current values with get_ad_account_finance."},"create_flow_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"description":{"type":"string","maxLength":500},"inputs":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"label":{"type":"string","minLength":1,"maxLength":80},"type":{"type":"string","enum":["text","textarea","number","select","image","video","audio","boolean"]},"required":{"type":"boolean"},"default":{},"description":{"type":"string","maxLength":500},"options":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":4000},"label":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","maxLength":300}},"required":["value","label"]}}},"required":["key","label","type"]}},"outputs":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"fromStep":{"type":"string","minLength":1},"path":{"type":"string","minLength":1},"label":{"type":"string","maxLength":80}},"required":["key","fromStep","path"]}},"steps":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"skillId":{"type":"string","minLength":1},"input":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"object","properties":{"literal":{}},"required":["literal"],"additionalProperties":false},{"type":"object","properties":{"fromStep":{"type":"string","minLength":1},"path":{"type":"string","minLength":1}},"required":["fromStep","path"],"additionalProperties":false},{"type":"object","properties":{"fromInput":{"type":"string","minLength":1}},"required":["fromInput"],"additionalProperties":false},{"type":"object","properties":{"template":{"type":"string","maxLength":20000}},"required":["template"],"additionalProperties":false}]}},"dependsOn":{"type":"array","items":{"type":"string"}},"gate":{"type":"boolean"},"label":{"type":"string","maxLength":80}},"required":["id","skillId","input"]}}},"required":["name","steps"],"description":"Saves a reusable flow: a named pipeline of up to 25 skill steps that runs as one unit. Steps form a graph: a step value is {\"literal\": value}, {\"fromStep\": \"step-id\", \"path\": \"dot.path\"} to read an earlier step's result (asynchronous maker steps complete as { status: \"completed\", deliverable }, so \"deliverable.files.0.url\" is the finished video and \"deliverable.files.*.url\" the list of files; synchronous steps like write_text expose their raw result, for example \"json.keyframes\"), {\"fromInput\": \"key\"} to read a flow input, or {\"template\": \"text with {{input.key}} and {{step-id.path}}\"} to compose a prompt (templates also support {{#if path}}...{{/if}} and {{#each list as item}}...{{/each}} with {{@index}} and {{@next.field}}). Declare run-time parameters in inputs ({key, label, type: text|textarea|number|select|image|video|audio|boolean, required, default, options}) and the flow's results in outputs ({key, fromStep, path}). A step with \"gate\": true pauses the run for a human approval before it spends anything (approve_flow_step resumes it). Reference nodes can also sit inside an object or array literal (a few levels deep), which is how a publish step's media object takes a finished video via {\"source\": \"url\", \"url\": {\"fromStep\": \"make-ad\", \"path\": \"deliverable.files.0.url\"}}. The building blocks are write_text, generate_images, animate_images and merge_videos: read a template flow with get_flow to see a complete example. Every step is validated against the real skill registry at save time. Building a flow is how you create a reusable skill for this brand: saved flows are reusable pipelines the user can run again and again with run_flow. Present the flow's steps and its total credit estimate (1 credit = 1 cent) to the user before running it."},"update_flow_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"description":{"type":"string","maxLength":500},"inputs":{"maxItems":20,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"label":{"type":"string","minLength":1,"maxLength":80},"type":{"type":"string","enum":["text","textarea","number","select","image","video","audio","boolean"]},"required":{"type":"boolean"},"default":{},"description":{"type":"string","maxLength":500},"options":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":4000},"label":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","maxLength":300}},"required":["value","label"]}}},"required":["key","label","type"]}},"outputs":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$"},"fromStep":{"type":"string","minLength":1},"path":{"type":"string","minLength":1},"label":{"type":"string","maxLength":80}},"required":["key","fromStep","path"]}},"steps":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"skillId":{"type":"string","minLength":1},"input":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"object","properties":{"literal":{}},"required":["literal"],"additionalProperties":false},{"type":"object","properties":{"fromStep":{"type":"string","minLength":1},"path":{"type":"string","minLength":1}},"required":["fromStep","path"],"additionalProperties":false},{"type":"object","properties":{"fromInput":{"type":"string","minLength":1}},"required":["fromInput"],"additionalProperties":false},{"type":"object","properties":{"template":{"type":"string","maxLength":20000}},"required":["template"],"additionalProperties":false}]}},"dependsOn":{"type":"array","items":{"type":"string"}},"gate":{"type":"boolean"},"label":{"type":"string","maxLength":80}},"required":["id","skillId","input"]}},"flowId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["name","steps","flowId"],"description":"Replaces a saved flow's name, description, inputs, outputs and steps with a new definition (same shape as create_flow), re-validated against the skill registry. Use it to iterate on a flow conversationally: fetch it with get_flow, adjust the steps or prompts, and save. Only the flow's owner can update it. Free."},"list_flows_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the user's saved flows: id, name, description, step count and credit estimate. Free and read-only. Saved flows are reusable pipelines; check here before building a new flow from scratch, and use get_flow to read one in full."},"get_flow_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["flowId"],"description":"Reads one saved flow in full: its inputs, outputs, every step with its skill, prompts and wiring, plus the credit estimate. Free and read-only. Use it to explain how a flow (or a template flow) works, or when the user wants the same pipeline again with a change: read the definition, adjust it in conversation, and save with update_flow instead of rebuilding from scratch."},"run_flow_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"inputs":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"targetStepIds":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"string"}},"reuseFromRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"confirm":{"type":"boolean"},"confirmed":{"type":"boolean"}},"required":["flowId"],"description":"Starts a saved flow. Your saved flow is a parameterized skill: pass inputs keyed by the flow's declared input keys (get_flow lists them, with types and required flags), for example { \"product_name\": \"Almond Mist\", \"brief\": \"Tonight's angle\" }. Flows without declared inputs take per-step literal overrides keyed by step id then input field instead. To re-run part of a flow, pass targetStepIds (only those steps and what they depend on run) and reuseFromRunId (unchanged upstream steps are copied from that run at no charge): the answer lists which steps execute, reuse and skip. Without confirm: true it answers needs_confirmation with the run's credit estimate and runs nothing; present that estimate to the user and start again with confirm: true only after they agree (1 credit = 1 cent). A step marked gate pauses the run for approval: show the user the results so far and call approve_flow_step. Once started, the flow runs step by step in the background: poll it with get_flow_run using the flowRunId, and come back later for long pipelines."},"run_flow_batch_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"variants":{"minItems":2,"maxItems":20,"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":80},"inputs":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"overrides":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"required":["label"]}},"confirm":{"type":"boolean"},"confirmed":{"type":"boolean"}},"required":["flowId","variants"],"description":"Runs a saved flow as a batch of 2 to 20 variants: turn one proven flow into a testing batch by swapping hooks, actors, products, or languages systematically. Each variant carries a label and per-step literal-input overrides keyed by step id then input field, for example { \"label\": \"Spanish hook\", \"overrides\": { \"make-ad\": { \"script\": \"Hola! ...\" } } }. Only literal inputs can be overridden; reference inputs cannot. Without confirm: true it answers needs_confirmation with the total credit estimate plus a per-variant breakdown and starts nothing; present that per-variant table and the total to the user, then call again with confirm: true only after they agree (1 credit = 1 cent). Once started, every variant runs as its own flow run sharing one batchId: poll individual runs with get_flow_run using each runId, and come back later for long batches."},"get_flow_run_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["flowRunId"],"description":"Checks a flow run started with run_flow: overall status, each step's state (keyed by step id, including awaiting_approval pauses and steps reused from an earlier run), currentSteps, awaitingApproval (steps paused at a gate), the deliverables of completed maker steps and the results of completed writer steps, both keyed by step id, and outputs (the flow's declared outputs, keyed by output key). Same shape as GET /api/v1/flow-runs/{runId}. Polling also nudges the run forward, so this is how flows execute in the background: start with run_flow, come back later and poll here. Free and safe to poll every minute or two."},"approve_flow_step_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"stepIds":{"minItems":1,"type":"array","items":{"type":"string"}}},"required":["flowRunId"],"description":"Releases a flow run paused at a gate (a step saved with gate: true, typically the clip step after the keyframes are ready) so it continues and spends that step's credits. Only call it after the user has reviewed the results so far and agreed; get_flow_run lists the waiting steps in awaitingApproval. Free; the released step's own cost was part of the run's estimate."},"cancel_flow_run_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"flowRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["flowRunId"],"description":"Ends a flow run that is paused at a gate or between steps, so nothing more is spent. A run with a clip still generating cannot be cancelled (that spend is real); wait for it, then cancel. Free."},"write_text_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"systemPrompt":{"type":"string","minLength":1,"maxLength":20000},"prompt":{"type":"string","minLength":1,"maxLength":20000},"format":{"default":"text","type":"string","enum":["text","json"]},"writerModel":{"default":"grok-4-6","type":"string","enum":["grok-4-6","gemini-flash","gemini-pro"]},"scenes":{"default":1,"type":"integer","minimum":1,"maximum":24},"maxTokens":{"type":"integer","minimum":256,"maximum":16000},"validate":{"type":"object","properties":{"kind":{"type":"string","enum":["storyboard","scene_storyboard","podcast_solo","podcast_conversation","talking_head","story_animation","in_car","music_video"]},"avoid":{"default":[],"maxItems":50,"type":"array","items":{"type":"string"}},"requireOnScreenText":{"type":"boolean"},"compose":{"type":"string","enum":["paint","chalkboard","story_animation","music_video"]},"style":{"type":"string","maxLength":4000}},"required":["kind"]},"confirmed":{"type":"boolean"}},"required":["systemPrompt","prompt"],"description":"Writes text or JSON with the writing model: a script, a storyboard, a caption, a list of prompts. Give it a systemPrompt (the role and the rules) and a prompt (the brief). With format \"json\" the answer is parsed and returned as json (one correction retry); with validate it is checked as a storyboard of the given kind and scene count, exactly like the Templates writer, and banned terms are refused. Returns text (and json) synchronously, so it needs no polling. The writer is included at no credit cost today; any writer surcharge is quoted before it is charged. In a flow, feed its json to generate_images and animate_images."},"generate_images_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompts":{"minItems":1,"maxItems":12,"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":4000},{"type":"object","properties":{"image_prompt":{"type":"string","minLength":1,"maxLength":4000},"uses_product":{"type":"boolean"},"uses_mascot":{"type":"boolean"},"uses_creator":{"type":"boolean"}},"required":["image_prompt"],"additionalProperties":{}}]}},"references":{"default":[],"maxItems":4,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000},"flag":{"type":"string","enum":["uses_product","uses_mascot","uses_creator"]}},"required":["url"]}},"imageModel":{"default":"gpt-image-2.5-sunburst","type":"string","enum":["gpt-image-2.5-sunburst","nano-banana-2","gpt-image-2"]},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9","1:1"]},"imageCount":{"type":"integer","minimum":1,"maximum":12},"confirmed":{"type":"boolean"}},"required":["prompts"],"description":"Generates a batch of images, one per prompt (up to 12), on the image model of your choice. Prompts can be plain strings or storyboard keyframes straight from write_text (their image_prompt is used, and their uses_product, uses_mascot and uses_creator flags decide which reference image each one gets). References are image urls: unflagged ones condition every image, flagged ones only the prompts that set that flag. Images come out of the monthly image allowance, not credits, so the credit cost is 0. Returns a runId to poll with check_run; the finished run lists the images in prompt order. In a flow, feed the images to animate_images as frames."},"animate_images_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"frames":{"minItems":1,"maxItems":13,"type":"array","items":{"anyOf":[{"type":"string","maxLength":2000},{"type":"object","properties":{"url":{"type":"string","maxLength":2000}},"required":["url"],"additionalProperties":{}}]}},"prompts":{"maxItems":12,"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":4000},{"type":"object","properties":{"motion_prompt":{"type":"string","minLength":1,"maxLength":4000}},"required":["motion_prompt"],"additionalProperties":{}},{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":4000}},"required":["prompt"],"additionalProperties":{}}]}},"promptTemplate":{"type":"string","maxLength":20000},"promptItems":{"maxItems":12,"type":"array","items":{}},"pairing":{"default":"start_frame","type":"string","enum":["start_frame","start_end_frames"]},"videoModel":{"default":"seedance25","type":"string"},"resolution":{"default":"720p","type":"string","enum":["480p","720p","768p","1080p"]},"durationSeconds":{"default":5,"type":"integer","minimum":1,"maximum":30},"aspect":{"default":"9:16","type":"string","enum":["9:16","16:9","1:1"]},"clipCount":{"type":"integer","minimum":1,"maximum":12},"confirmed":{"type":"boolean"}},"required":["frames"],"description":"Turns a batch of images into video clips, one clip per frame (up to 12), on an image-to-video model. Pass the frames in order (urls, or the images from a generate_images run) and one prompt per clip, or a promptTemplate rendered per item of promptItems so a storyboard becomes the clip prompts without another writing pass. pairing \"start_end_frames\" interpolates each frame into the next (clips = frames minus one), the Product Ad style; \"start_frame\" animates each frame on its own. Every clip costs the same as one make_image_to_video clip of that model, resolution and duration, and the whole batch is quoted before anything is charged: without confirmed: true it answers needs_confirmation and runs nothing. Returns a runId to poll with check_run; the finished run lists the clips in order, ready for merge_videos."},"save_asset_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["image","video","webpage","audio","other"]},"title":{"type":"string","minLength":1,"maxLength":200},"sourceUrl":{"type":"string","maxLength":2000,"format":"uri"},"storagePath":{"type":"string","minLength":1,"maxLength":500},"tags":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":60}},"analysis":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["kind","title"],"description":"Saves a reusable asset (image, video, webpage, audio) to the user's asset library. Your asset library is your memory of reusable material: whenever the user shares a product photo, video, page or audio worth reusing, or you analyze one with analyze_asset, save it here with a meaningful title and tags so future conversations can find it with list_assets instead of asking the user again. Pass a public sourceUrl, or a storagePath: to save a local file the user gave you, POST /api/v1/uploads (Bearer key) with { kind, filename }, PUT the bytes to the returned uploadUrl, then save the returned storagePath here. Pass the analysis through when you already analyzed it. Free."},"list_assets_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["image","video","webpage","audio","other"]},"tag":{"type":"string","minLength":1,"maxLength":60},"search":{"type":"string","minLength":1,"maxLength":120},"limit":{"default":50,"type":"integer","minimum":1,"maximum":50}},"description":"Lists the user's saved assets (newest first, up to 50), optionally filtered by kind, tag, or a title text match, including any stored analysis. Stored assets also come back with a fresh 24 hour url you can hand straight to a maker skill. Free and read-only. Your asset library is your memory of reusable material: check list_assets for existing product photos, videos or pages before asking the user to re-upload or re-share anything."},"analyze_asset_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"url":{"type":"string","maxLength":2000,"format":"uri"},"kind":{"type":"string","enum":["image","video","webpage","audio","other"]}},"description":"Analyzes an asset so it can be reused in production: images get a vision analysis (subject, style, colors, visible text, suggested uses), videos get a transcription PLUS a visual description (what is on screen: subject, setting, framing, style), audio gets a transcription, and webpages get the site scrape plus brand draft. Analysis costs 1 to 3 credits depending on the kind (1 credit = 1 cent); webpage analysis is included at no cost. Pass an assetId to persist the analysis onto the saved asset, or a url plus kind for new material, then save_asset it with a meaningful title and tags so future conversations can reuse it."},"create_project_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200}},"description":"Creates a project: a reusable multi-clip video that clips are added to and then assembled into one final video. Free. Start here when the user wants to combine several clips into one video, then add finished clips with add_clip."},"get_project_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["projectId"],"description":"Returns a project with its clips in order, each resolved to a playable video URL, plus the final video once assembled. Free and read-only. Use it to show the user the current clip order before reordering, trimming, or finalizing."},"list_projects_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"description":"Lists the user's projects (multi-clip video artifacts), newest first, with a clip count and status each. Free and read-only. Check here before creating a new project so an existing one can be reused."},"add_clip_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"source":{"type":"object","properties":{"kind":{"type":"string","enum":["run","url","upload"]},"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"url":{"type":"string","format":"uri"},"storagePath":{"type":"string","minLength":1}},"required":["kind"]},"position":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["projectId","source"],"description":"Adds one clip to a project. Pass a finished run's runId (the usual case, straight from a completed maker skill), a direct video URL, or an owned upload path. Free. Appends at the end unless you set a position. Tell the user which clip you added so they can watch the project build up."},"reorder_clips_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orderedClipIds":{"minItems":1,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["projectId","orderedClipIds"],"description":"Sets the order of a project's clips. Pass every clip id exactly once in the new order. Free. Present the new order to the user and confirm it before assembling, since order is how the final video plays."},"trim_clip_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"clipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trimStartSeconds":{"type":"number","minimum":0},"trimEndSeconds":{"type":"number","minimum":0}},"required":["clipId"],"description":"Records trim points (start and/or end seconds) on a project clip. Free. Trims are recorded on the project as metadata and are not yet applied when the project is assembled, so tell the user the trims are saved but the assembled video still uses each clip in full for now."},"remove_clip_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"clipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["clipId"],"description":"Removes a clip from its project and renumbers the rest. Free. Use it to drop a clip the user does not want in the assembled video."},"finalize_project_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"confirmed":{"type":"boolean"}},"required":["projectId"],"description":"Assembles a project into one final video by merging its clips in order. Included at no credit cost. ALWAYS present the clip order to the user and confirm it before finalizing. Returns a runId that you poll with check_run; when it completes the project is ready with a final video URL that can then be published with publish_video or captioned with add_captions."},"check_run_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"skillRunId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"description":"Checks the status of a run started by another skill (pass the runId it returned) and returns the finished deliverable (video URL, cost, timing) when it completes. Free and safe to poll; generation runs typically take 6 to 25 minutes, so poll every minute or two rather than in a tight loop."},"list_runs_input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"limit":{"default":20,"type":"integer","minimum":1,"maximum":100},"apiKeyId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["running","awaiting_confirmation","completed","failed","cancelled"]},"skillId":{"type":"string"}},"description":"Lists the user's most recent skill runs, newest first: skill, status, cost in credits (1 credit = 1 cent), which API key made the call, and timestamps. Failed runs carry errorCode plus the error message, including calls rejected before execution (bad input, unknown skill, budget caps), so this is the place to debug an integration. Free and read-only; filter with apiKeyId, status or skillId, and poll a single run with check_run."}}},"paths":{"/api/v1/skills":{"get":{"operationId":"listSkills","summary":"List the available skills with schemas, costs and durations","x-required-scope":"read","responses":{"200":{"description":"The skill catalog.","content":{"application/json":{"schema":{"type":"object","required":["skills"],"properties":{"skills":{"type":"array","items":{"type":"object","required":["id","displayName","requiredScope","description","creditCostRange","durationBand","inputSchema"],"properties":{"id":{"type":"string","enum":["list_templates","list_models","list_actors","list_voices","get_brand","update_brand","list_platform_accounts","analyze_website","make_ugc_ad","make_product_ad","make_explainer","make_talking_head","make_image_to_video","make_voiceover","add_voiceover","add_captions","merge_videos","create_actor","clone_voice","approve_storyboard","publish_video","schedule_post","get_performance","list_ad_accounts","list_ad_campaigns","get_ad_campaign","get_ad_performance","get_ad_account_timeline","get_ad_account_finance","get_ad_activity","list_ad_pixels","search_ad_targeting","estimate_ad_reach","preview_ad","list_boostable_posts","launch_ad","boost_post","set_ad_status","pause_ad","bulk_ad_campaign_status","update_ad_budget","rename_ad_campaign","duplicate_ad_campaign","duplicate_ad","update_ad_creative","archive_ad_campaign","cancel_ad","set_ad_account_defaults","create_flow","update_flow","list_flows","get_flow","run_flow","run_flow_batch","get_flow_run","approve_flow_step","cancel_flow_run","write_text","generate_images","animate_images","save_asset","list_assets","analyze_asset","create_project","get_project","list_projects","add_clip","reorder_clips","trim_clip","remove_clip","finalize_project","check_run","list_runs"]},"displayName":{"type":"string"},"requiredScope":{"type":"string","enum":["read","skills:run","flows:write","publish"],"description":"The API key scope needed to run this skill."},"description":{"type":"string"},"creditCostRange":{"type":"object","properties":{"minCents":{"type":"integer"},"maxCents":{"type":"integer"}}},"durationBand":{"type":"object","properties":{"p50Seconds":{"type":"number"},"p90Seconds":{"type":"number"}}},"inputSchema":{"type":"object"},"custom":{"type":"boolean","description":"True for the caller's own saved flows exposed as skills. These are per-user, so they appear in this listing (and over MCP) but never in this static spec."}}}}}}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription, or the key lacks the read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/skills/{skillId}/run":{"post":{"operationId":"runSkill","summary":"Run a skill","description":"Starts a skill run. Maker skills spend real credits and may answer with status needs_confirmation, in which case the user must approve the estimated cost and the call is repeated with confirmed: true. Send an Idempotency-Key header to make a retry safe: the same key returns the same response and charges once. The body also accepts one top-level field outside the skill schema: webhookUrl, a per-run completion-webhook override delivered with the key's signing secret (register one first with PUT /api/v1/webhook). Each skill requires its own scope on the key (see requiredScope in GET /api/v1/skills): read skills need read, makers and project edits need skills:run, flow editing needs flows:write, publishing and ads need publish.","x-required-scope":"per skill; see requiredScope in GET /api/v1/skills","parameters":[{"name":"skillId","in":"path","required":true,"schema":{"type":"string","description":"A skill id from GET /api/v1/skills: either a registry skill (lowercase words joined by underscores, for example make_ugc_ad) or one of the caller's own saved flows, whose ids start with \"flow_\" and are equally valid here.","examples":["list_templates","list_models","list_actors","list_voices","get_brand"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"The skill's own input object. Custom flow skills (\"flow_\" ids) take { inputs?, confirm? } instead of one of the schemas below.","oneOf":[{"$ref":"#/components/schemas/list_templates_input"},{"$ref":"#/components/schemas/list_models_input"},{"$ref":"#/components/schemas/list_actors_input"},{"$ref":"#/components/schemas/list_voices_input"},{"$ref":"#/components/schemas/get_brand_input"},{"$ref":"#/components/schemas/update_brand_input"},{"$ref":"#/components/schemas/list_platform_accounts_input"},{"$ref":"#/components/schemas/analyze_website_input"},{"$ref":"#/components/schemas/make_ugc_ad_input"},{"$ref":"#/components/schemas/make_product_ad_input"},{"$ref":"#/components/schemas/make_explainer_input"},{"$ref":"#/components/schemas/make_talking_head_input"},{"$ref":"#/components/schemas/make_image_to_video_input"},{"$ref":"#/components/schemas/make_voiceover_input"},{"$ref":"#/components/schemas/add_voiceover_input"},{"$ref":"#/components/schemas/add_captions_input"},{"$ref":"#/components/schemas/merge_videos_input"},{"$ref":"#/components/schemas/create_actor_input"},{"$ref":"#/components/schemas/clone_voice_input"},{"$ref":"#/components/schemas/approve_storyboard_input"},{"$ref":"#/components/schemas/publish_video_input"},{"$ref":"#/components/schemas/schedule_post_input"},{"$ref":"#/components/schemas/get_performance_input"},{"$ref":"#/components/schemas/list_ad_accounts_input"},{"$ref":"#/components/schemas/list_ad_campaigns_input"},{"$ref":"#/components/schemas/get_ad_campaign_input"},{"$ref":"#/components/schemas/get_ad_performance_input"},{"$ref":"#/components/schemas/get_ad_account_timeline_input"},{"$ref":"#/components/schemas/get_ad_account_finance_input"},{"$ref":"#/components/schemas/get_ad_activity_input"},{"$ref":"#/components/schemas/list_ad_pixels_input"},{"$ref":"#/components/schemas/search_ad_targeting_input"},{"$ref":"#/components/schemas/estimate_ad_reach_input"},{"$ref":"#/components/schemas/preview_ad_input"},{"$ref":"#/components/schemas/list_boostable_posts_input"},{"$ref":"#/components/schemas/launch_ad_input"},{"$ref":"#/components/schemas/boost_post_input"},{"$ref":"#/components/schemas/set_ad_status_input"},{"$ref":"#/components/schemas/pause_ad_input"},{"$ref":"#/components/schemas/bulk_ad_campaign_status_input"},{"$ref":"#/components/schemas/update_ad_budget_input"},{"$ref":"#/components/schemas/rename_ad_campaign_input"},{"$ref":"#/components/schemas/duplicate_ad_campaign_input"},{"$ref":"#/components/schemas/duplicate_ad_input"},{"$ref":"#/components/schemas/update_ad_creative_input"},{"$ref":"#/components/schemas/archive_ad_campaign_input"},{"$ref":"#/components/schemas/cancel_ad_input"},{"$ref":"#/components/schemas/set_ad_account_defaults_input"},{"$ref":"#/components/schemas/create_flow_input"},{"$ref":"#/components/schemas/update_flow_input"},{"$ref":"#/components/schemas/list_flows_input"},{"$ref":"#/components/schemas/get_flow_input"},{"$ref":"#/components/schemas/run_flow_input"},{"$ref":"#/components/schemas/run_flow_batch_input"},{"$ref":"#/components/schemas/get_flow_run_input"},{"$ref":"#/components/schemas/approve_flow_step_input"},{"$ref":"#/components/schemas/cancel_flow_run_input"},{"$ref":"#/components/schemas/write_text_input"},{"$ref":"#/components/schemas/generate_images_input"},{"$ref":"#/components/schemas/animate_images_input"},{"$ref":"#/components/schemas/save_asset_input"},{"$ref":"#/components/schemas/list_assets_input"},{"$ref":"#/components/schemas/analyze_asset_input"},{"$ref":"#/components/schemas/create_project_input"},{"$ref":"#/components/schemas/get_project_input"},{"$ref":"#/components/schemas/list_projects_input"},{"$ref":"#/components/schemas/add_clip_input"},{"$ref":"#/components/schemas/reorder_clips_input"},{"$ref":"#/components/schemas/trim_clip_input"},{"$ref":"#/components/schemas/remove_clip_input"},{"$ref":"#/components/schemas/finalize_project_input"},{"$ref":"#/components/schemas/check_run_input"},{"$ref":"#/components/schemas/list_runs_input"}]}}}},"responses":{"200":{"description":"The result envelope: the same shape whether the run started, finished, or needs a cost confirmation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EngineResult"}}}},"400":{"description":"The input failed validation; see error.issues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits, or the API key budget cap was exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Subscription requirements not met, or the key lacks the scope this skill requires (insufficient_scope).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown skill id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict: idempotency_key_reused when the same key arrives with a different body, or idempotency_key_in_progress when the original call is still running (retry after the seconds in Retry-After).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/uploads":{"post":{"operationId":"createUpload","summary":"Mint a signed upload URL for a local file","x-required-scope":"skills:run","description":"Returns a short-lived signed URL for pushing a file into the caller's own storage. PUT the raw bytes to uploadUrl with the file content type as the Content-Type header within 2 hours, then pass the returned storagePath to a skill: save_asset for anything, create_actor for images (same path works as originalPath and processedPath), clone_voice for voice samples.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","filename"],"properties":{"kind":{"type":"string","enum":["video","image","audio","voice"],"description":"Target bucket: video, image, audio, or voice for a voice-cloning sample."},"filename":{"type":"string","minLength":1,"maxLength":200,"description":"Original filename; used for the extension and a readable path."}}}}}},"responses":{"201":{"description":"The signed upload target.","content":{"application/json":{"schema":{"type":"object","required":["upload"],"properties":{"upload":{"type":"object","required":["kind","bucket","storagePath","uploadUrl","token"],"properties":{"kind":{"type":"string","enum":["video","image","audio","voice"]},"bucket":{"type":"string"},"storagePath":{"type":"string","description":"Pass this to the skill that takes a storagePath."},"uploadUrl":{"type":"string","format":"uri"},"token":{"type":"string"},"maxBytes":{"type":"integer"},"expiresInSeconds":{"type":"integer"},"howToUse":{"type":"string"},"createActorHint":{"type":"string","description":"Present for image uploads."}}}}}}}},"400":{"description":"Invalid kind or filename.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription, or the key lacks the skills:run scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The upload URL could not be created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/runs":{"get":{"operationId":"listRuns","x-required-scope":"read","summary":"List recent runs, newest first","description":"The run log for this account: every skill run with its status, cost and API key, including calls rejected before execution (bad input, unknown skill, budget caps). Failed runs carry errorCode and the error message.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"apiKeyId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only runs made with this API key."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["running","awaiting_confirmation","completed","failed","cancelled"]}},{"name":"skillId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The recent runs, newest first.","content":{"application/json":{"schema":{"type":"object","required":["runs"],"properties":{"runs":{"type":"array","items":{"type":"object","required":["runId","skill","status","surface","createdAt","checkUrl"],"properties":{"runId":{"type":"string","format":"uuid"},"skill":{"type":"string"},"status":{"type":"string"},"surface":{"type":"string"},"estimatedCents":{"type":"integer"},"chargedCents":{"type":"integer"},"errorCode":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"apiKey":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"prefix":{"type":"string","nullable":true}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","nullable":true},"checkUrl":{"type":"string","format":"uri"}}}}}}}}},"400":{"description":"Invalid filter values.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/runs/{runId}":{"get":{"operationId":"getRun","summary":"Check a run and fetch its files when complete","x-required-scope":"read","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The result envelope for this run, with its files and cost once completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EngineResult"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No run with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhook":{"get":{"operationId":"getWebhookConfig","x-required-scope":"any valid API key; webhooks are per key","summary":"Read the calling key's completion-webhook config","description":"Completion webhooks are the alternative to polling GET /api/v1/runs/{runId}: register a URL and every state change of this key's runs (completed, failed, cancelled, awaiting_approval, awaiting_confirmation) is POSTed to it as a signed WebhookEvent, with retries. The secret is returned masked here.","responses":{"200":{"description":"The current config, secret masked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookConfig"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"setWebhook","x-required-scope":"skills:run","summary":"Register or update the calling key's completion webhook","description":"Registers the callback URL for the calling key and mints its whsec_... signing secret on first use. Omit url to only mint the secret (enough to pass per-run webhookUrl on skill runs). The secret is returned in plaintext only when it was just created or when regenerateSecret is true: store it then. Any PUT also re-enables an endpoint that was auto-disabled after repeated delivery failures.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"https only; private and internal hosts are rejected."},"regenerateSecret":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The saved config; see the secret field rules.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookConfig"}}}},"400":{"description":"The URL is invalid, not https, or points at a private host.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope requirements not met.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteWebhookUrl","x-required-scope":"skills:run","summary":"Remove the calling key's webhook URL (the secret is kept)","responses":{"200":{"description":"The URL was removed.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":["string","null"]},"message":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys":{"get":{"operationId":"listApiKeys","x-required-scope":"session only; API keys cannot manage keys","summary":"List API keys (session auth, not key auth)","description":"Uses the logged-in web session, not an API key. Hashes are never returned.","security":[],"responses":{"200":{"description":"The API keys for the logged-in user.","content":{"application/json":{"schema":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Not logged in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createApiKey","x-required-scope":"session only; API keys cannot manage keys","summary":"Create an API key (session auth, not key auth)","description":"Uses the logged-in web session and requires an active Pro or Premium subscription. The plaintext key is returned exactly once.","security":[],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"budgetCapCents":{"type":"integer","minimum":1,"description":"Optional per-key spending cap in credits."},"scopes":{"type":"array","minItems":1,"items":{"type":"string","enum":["read","skills:run","flows:write","publish"]},"description":"Scopes granted to the key. Defaults to read and skills:run."},"expiresAt":{"type":"string","format":"date-time","description":"Optional expiration instant (must be in the future). Omit for a key that never expires."}}}}}},"responses":{"201":{"description":"The new key. Store the plaintext now; it is never shown again.","content":{"application/json":{"schema":{"type":"object","required":["id","name","prefix","scopes","key","message"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":["string","null"],"format":"date-time"},"key":{"type":"string"},"message":{"type":"string"}}}}}},"401":{"description":"Not logged in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys/{keyId}":{"delete":{"operationId":"revokeApiKey","x-required-scope":"session only; API keys cannot manage keys","summary":"Revoke an API key (session auth, not key auth)","security":[],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The key was revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"}}}}}},"401":{"description":"Not logged in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No key with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flows":{"get":{"operationId":"listFlows","summary":"List your saved flows","x-required-scope":"read","responses":{"200":{"description":"The saved flows.","content":{"application/json":{"schema":{"type":"object","required":["flows"],"properties":{"flows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"stepCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription, or the key lacks the read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createFlow","summary":"Save a flow (a validated DAG of skill steps)","x-required-scope":"flows:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDefinition"}}}},"responses":{"201":{"description":"The flow was saved.","content":{"application/json":{"schema":{"type":"object","required":["flow","estimate"],"properties":{"flow":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"stepCount":{"type":"integer"}}},"estimate":{"$ref":"#/components/schemas/FlowEstimate"}}}}}},"400":{"description":"The definition failed validation; see error.issues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription, or the key lacks the flows:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flows/{flowId}":{"get":{"operationId":"getFlow","summary":"Fetch one saved flow with its full definition","parameters":[{"name":"flowId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The saved flow.","content":{"application/json":{"schema":{"type":"object","required":["flow"],"properties":{"flow":{"type":"object","required":["id","name","definition","estimate","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"definition":{"$ref":"#/components/schemas/FlowDefinition"},"estimate":{"$ref":"#/components/schemas/FlowEstimate"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"updateFlow","summary":"Replace the definition of a saved flow","description":"The whole definition is replaced and re-validated, exactly like creating one. Fetch the current definition with GET first if you are editing rather than rewriting.","parameters":[{"name":"flowId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDefinition"}}}},"responses":{"200":{"description":"The flow was updated.","content":{"application/json":{"schema":{"type":"object","required":["flow","estimate"],"properties":{"flow":{"type":"object","required":["id","name","stepCount"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"stepCount":{"type":"integer"}}},"estimate":{"$ref":"#/components/schemas/FlowEstimate"}}}}}},"400":{"description":"The definition failed validation; see error.issues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The API requires an active Pro or Premium subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flows/{flowId}/run":{"post":{"operationId":"runFlow","summary":"Start a flow run","x-required-scope":"skills:run","description":"Flows spend real credits. Without confirm: true the response is status needs_confirmation with the total estimate, the per-step breakdown and the plan; repeat the call with confirm: true (and the same inputs) after the user approves. Once confirmed, every step runs pre-confirmed against the flow total, while tier budgets still apply per step. Pass inputs for the flow's declared inputs, targetStepIds to produce only some steps (their upstream steps run too), and reuseFromRunId to copy the completed, unchanged steps of an earlier run of the same flow at no charge. Send an Idempotency-Key header to make a retry safe: the same key returns the same response and starts one flow run.","parameters":[{"name":"flowId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"boolean","description":"Set true only after the user approved the estimated total."},"inputs":{"type":"object","description":"Values for the flow's declared inputs, keyed by input key.","additionalProperties":{}},"targetStepIds":{"type":"array","minItems":1,"items":{"type":"string"},"description":"Produce only these steps (and what they depend on); the rest are skipped."},"reuseFromRunId":{"type":"string","format":"uuid","description":"An earlier run of this flow (younger than 24 hours) whose completed, unchanged steps are copied instead of re-run."}}}}}},"responses":{"200":{"description":"The run started, or needs a cost confirmation.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["flowRunId","status","totalEstimatedCents","plan","checkUrl"],"properties":{"flowRunId":{"type":"string","format":"uuid"},"status":{"type":"string"},"totalEstimatedCents":{"type":"integer"},"plan":{"$ref":"#/components/schemas/FlowRunPlan"},"checkUrl":{"type":"string","format":"uri"}}},{"type":"object","required":["status","totalEstimatedCents","perStep","plan","message","howToConfirm"],"properties":{"status":{"type":"string","const":"needs_confirmation"},"totalEstimatedCents":{"type":"integer"},"unknownSteps":{"type":"array","items":{"type":"string"}},"perStep":{"type":"object","description":"Estimated credits per step id; null when the step is priced at runtime.","additionalProperties":{"type":["integer","null"]}},"plan":{"$ref":"#/components/schemas/FlowRunPlan"},"message":{"type":"string"},"howToConfirm":{"type":"string"}}}]}}}},"400":{"description":"The saved definition is no longer valid (see error.issues), the inputs do not satisfy the flow, or the body is malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits, or the API key budget cap was exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Subscription requirements not met, or the key lacks the skills:run scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency conflict: idempotency_key_reused when the same key arrives with a different body, or idempotency_key_in_progress when the original call is still running (retry after the seconds in Retry-After).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flow-runs/{runId}":{"get":{"operationId":"getFlowRun","summary":"Check a flow run: per-step states and finished deliverables","x-required-scope":"read","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The flow run status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRunReport"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow run with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flow-runs/{runId}/approve":{"post":{"operationId":"approveFlowSteps","summary":"Release the gated steps of a paused flow run","x-required-scope":"skills:run","description":"A step saved with gate: true parks the run at awaiting_confirmation before it starts (the step ids are in awaitingApproval). Approving releases the listed steps, or every gated step when stepIds is omitted, advances the run right away and answers the same report as GET /api/v1/flow-runs/{runId}.","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"stepIds":{"type":"array","items":{"type":"string"},"description":"Gated step ids to release; every gated step when omitted."}}}}}},"responses":{"200":{"description":"The run after the approval.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRunReport"}}}},"400":{"description":"stepIds is not a list of step ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Subscription requirements not met, or the key lacks the skills:run scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow run with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"not_gated: the run is finished, or a listed step is not waiting at a gate (the message names the steps that are).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/flow-runs/{runId}/cancel":{"post":{"operationId":"cancelFlowRun","summary":"Cancel a paused flow run without spending more","x-required-scope":"skills:run","description":"Ends a run that is not executing anything right now (parked at a gate, or between steps). A run with a step in flight cannot be cancelled: that spend is real and the step must finish or fail on its own. Answers the same report as GET /api/v1/flow-runs/{runId}.","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The cancelled run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRunReport"}}}},"401":{"description":"Missing, invalid or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Subscription requirements not met, or the key lacks the skills:run scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No flow run with this id belongs to this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"not_cancellable: the run already finished, or a step is executing at the provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}