Skip to content
maskostudioDocs

All pages

  • Overview/docs/use-cases
  • Mobile app/docs/use-cases#mobile
  • Website/docs/use-cases#website
  • Desktop/docs/use-cases#desktop
  • AI agents/docs/use-cases#agents
  • Videos/docs/use-cases#videos
  • Brand/docs/use-cases#brand
  • Start building/docs/ai-agents
  • Set up once/docs/ai-agents#set-up-once
  • Ask in plain words/docs/ai-agents#ask-in-plain-words
  • What your agent does/docs/ai-agents#what-your-agent-does
  • MCP server/docs/mcp
  • Masko for Cursor/docs/ai-tools/cursor
  • Masko in ChatGPT/docs/ai-tools/chatgpt
  • Start here/docs/tutorials#start-here
  • All tutorials/docs/tutorials
  • Create your first mascot/docs/tutorials/first-mascot
  • Create an animated character/docs/tutorials/first-animation
  • Make a character talk/docs/tutorials/make-it-talk
  • Put your mascot on your website/docs/tutorials/mascot-on-your-website
  • Guide onboarding with a talking mascot/docs/tutorials/web-onboarding-mascot
  • Add a mascot that celebrates progress and welcomes users back/docs/tutorials/web-progress-companion
  • Add a talking support mascot in the bottom right/docs/tutorials/web-support-mascot
  • Show a mascot while your AI thinks/docs/tutorials/ai-loading-mascot
  • Give your AI agent a face/docs/tutorials/ai-agent-face
  • Animate the mascot you already have/docs/tutorials/animate-your-mascot
  • How to make Telegram stickers/docs/tutorials/telegram-stickers
  • How to make Discord stickers/docs/tutorials/discord-stickers
  • Quickstart: your first animation/docs/quickstart
  • Terminal quickstart/docs/terminal
  • API keys and workspaces/docs/authentication
  • CLI browser login/docs/authentication/cli
  • How mascots work/docs/how-mascots-work
  • Credits and costs/docs/credits
  • From text/docs/create/from-text
  • From an image/docs/create/from-image
  • From a website/docs/create/from-website
  • From a brand book/docs/create/from-brand-book
  • Reference images/docs/manage/references
  • What you can generate/docs/generation
  • Pose-to-animation walkthrough/docs/generate/workflow
  • Images and poses/docs/generate/images
  • Animations/docs/generate/animations
  • Talking animations/docs/generate/talking
  • Logos, backgrounds and stickers/docs/generate/design-assets
  • Cursor follower/docs/generate/cursor-follower
  • Batch generation/docs/generate/batch
  • Image and video exports/docs/manage/media-exports
  • Canvases and releases/docs/canvas
  • Build a canvas/docs/canvas/build
  • Canvas templates/docs/canvas/templates
  • Generate canvas assets/docs/canvas/generate-all
  • Export a canvas/docs/canvas/export
  • Overview and demo/docs/player
  • Play one animation/docs/player/one-animation
  • Play a canvas/docs/player/canvas
  • Hosting and file formats/docs/generate/cdn
  • File sizes/docs/generate/file-sizes
  • Float a mascot on your Mac/docs/integrations/desktop
  • Jobs and polling/docs/manage/jobs
  • Webhooks/docs/manage/webhooks
  • Manage mascots and assets/docs/manage/collections
  • All endpoints/docs/reference
  • Requests and responses/docs/reference/conventions
  • Errors and recovery/docs/reference/errors
  • Command line reference/docs/terminal/commands
  • TypeScript SDK/docs/sdk/typescript
  • Changelog/docs/reference/changelog
  • Migrate to mascots and canvases/docs/manage/studio-migration
Get an API key
Documentation

Generate canvas assets

Generate a canvas's missing pose images and transition videos in one call. Plan with dry_run: true, then execute with the returned plan_id and graph_content_hash so a changed graph is rejected before credits are spent.

Use a write key for the mascot's workspace. Dry runs check the same permissions as generation, so a personal key cannot plan an organization canvas.

Read-only estimates

GET /v1/canvases/{canvasId}/generation-estimate returns the same plan and cost preview with a read-only key, without changing anything or spending credits. Pass the options as URL-encoded JSON in options:

const options = { animation_model: 'standard', duration: 5, targets: 'all' };
const query = new URLSearchParams({ options: JSON.stringify(options) });
const response = await fetch(
  `https://api.masko.ai/v1/canvases/${canvasId}/generation-estimate?${query}`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
);
const result = await response.json();
if (!response.ok) throw new Error(result.error.message);
const plan = result.data;
  • Defaults: Standard, 5 seconds, all missing media, no extra stickers. Completed media is always skipped.
  • node_ids and edge_ids select specific work; include_stickers adds sticker costs.
  • dry_run, approved_plan_id, expected_graph_hash and max_credits are rejected here.

To execute, POST the same options, with the explicit model and duration, to generate-all with a write key, dry_run: false, approved_plan_id: plan.plan_id, expected_graph_hash: plan.graph_content_hash and an approved max_credits.

For a graph with several character forms, set generationVariantId on each new node that should use an approved variant of the same mascot; untagged new nodes use the canvas identity. Existing poses keep their identity, a conflicting variant is rejected, and variant access is checked in the dry run. To finish an unfinished section with its stickers, send targets: "all", skip_completed: true and include_stickers: true.

curl -X POST https://api.masko.ai/v1/canvases/CANVAS_ID/generate-all \
  -H "Authorization: Bearer masko_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dry_run": true,
    "targets": "animations",
    "edge_ids": ["wave-entry", "wave-loop"],
    "skip_completed": true,
    "animation_model": "standard",
    "duration": 5
  }'

How It Works

targets picks node images, edge videos or both. skip_completed (default true) skips parts that already have assigned assets, so a second run never duplicates in-flight or completed work. node_ids and edge_ids limit the selection, and unknown IDs are rejected before any job or charge. A changed graph, prompt, option or selection returns 409.

Response fields:

  • dry_run: true when nothing was created or charged.
  • plan_id: identity of the exact graph, prompts, scope and options.
  • graph_content_hash: canvas revision the plan was made from.
  • planned_jobs: node, sticker and edge work with prompts. Animation and sticker jobs include signed source and target URLs when those images exist; media the run would create shows only its prompt and cost.
  • generated: newly queued node or edge jobs.
  • skipped: number of skipped edges. skipped_items lists every part not queued, filtered out by targets or already assigned.
  • reverse_free: reverse edges this run makes at 0 credits, by playing their forward clip backwards.
  • already_complete: parts whose assigned assets are completed.
  • estimated_cost, actual_cost, would_charge_credits: cost before dispatch, credits deducted, and the dry-run estimate.

Execution returns 200 OK with the queued jobs; generation runs asynchronously.

Edge Classification

TypeConditionCost
Loopsource == targetPaid
Forwardsource != targetPaid
ReverseReverse of a forward edge0 credits
Any Statesource = "*"Skipped: resolved at runtime, no video

Cost Calculation

Only loops and forward transitions cost credits: (loops + forwards) x credits per second x duration. animation_model: "standard" costs 2 credits/sec for 2–15 s and "premium" 6 credits/sec for 4–30 s, also for single-edge generation. Changing the model or duration after the dry run needs a new plan.

For example, 4 loops and 3 forwards on Standard at 5 seconds:

Paid edges: 4 loops + 3 forwards = 7
Cost per edge: 2 credits/sec x 5 sec = 10 credits
Total: 7 x 10 = 70 credits

Reverse edges (auto): 3 (one per forward) = 0 credits
Any State edges: skipped = 0 credits

Without enough credits, the request returns 402 with the required amount.

When a forward transition completes, Masko fills its reverse edge with the reversed video at 0 credits, with no separate request. Only reverse edges declared in the graph are filled, so declare each return path you want.

With edge_ids, listing either edge of a pair is enough: a forward edge brings its reverse, and a reverse whose forward clip is already finished is made from that clip. Until the forward edge has a finished clip, its reverse is listed in skipped_items with forward_clip_not_ready. A forward clip that cannot be reversed is listed with forward_clip_not_reversible; generate the forward edge again to make its reverse.

Poll Progress

Poll GET /v1/canvases/CANVAS_ID/status every 15 to 30 seconds for one summary of the whole canvas; read-only keys can call it. To follow one job, use the poll_url returned with it in jobs.

status.generation.nodes and .edges count total, completed, pending (neither finished nor failed) and failed parts. status.failed_nodes and status.failed_edges give each failed part, its job ID and a customer-safe error. A retry is a new paid attempt and should require approval.

curl https://api.masko.ai/v1/canvases/CANVAS_ID/status \
  -H "Authorization: Bearer masko_YOUR_API_KEY"

# Response:
# {
#   "data": {
#     "canvas_id": "abc-123",
#     "graph_content_hash": "sha256:…",
#     "status": {
#       "generation": {
#         "ready": false,
#         "nodes": { "total": 4, "completed": 4, "pending": 0, "failed": 0 },
#         "edges": { "total": 14, "completed": 10, "pending": 3, "failed": 1 }
#       },
#       "preview": { "ready": false, "waiting_edges": 0, "repairable": false, … },
#       "variants": { "sizes": [], "missing": [] },
#       "failed_nodes": [],
#       "failed_edges": [
#         { "edge_id": "wave-idle", "job_id": "…", "error": "The provider could not animate this pose." }
#       ],
#       "edge_media": [ … ]
#     }
#   }
# }

Regenerate a Single Edge

Re-run one edge after a failure or a prompt edit:

curl -X POST https://api.masko.ai/v1/canvases/CANVAS_ID/edges/EDGE_ID/generate \
  -H "Authorization: Bearer masko_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "animation_model": "standard",
    "duration": 5
  }'

It returns 202 Accepted with a job_id. Reverse edges cannot be generated here: list one in edge_ids on generate-all to make it from its forward clip.