Canvas & Templates
A canvas is an interactive state machine for your mascot. Define poses (nodes), transitions (edges), conditions, and inputs so the runtime can resolve app or user activity into mascot behavior.
Concepts
Create a Canvas
Create a canvas within a collection. A canvas starts empty, or you can pass a template_id to populate it with a pre-built set of nodes and edges in the same call.
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Canvas"
}'Create from a Template
Templates define a pre-built set of nodes and edges. Pass template_id to the same POST /canvases endpoint to populate the new canvas with poses and transitions and kick off image generation for each node.
# Create a new canvas from the Claude Code 4-state template
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Canvas",
"template_id": "claude-code-4state"
}'Extend a Graph Safely
Use graph/extend when you add a behavior to an existing canvas. It appends new graph parts and patches existing edges by ID, so generated videoAssetId values stay intact.
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/graph/extend \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"add_nodes": [{ "id": "interact", "x": 360, "y": 160, "itemName": "Interact" }],
"add_edges": [{
"id": "idle-to-interact",
"source": "idle",
"target": "interact",
"duration": 4,
"speed": 3,
"conditions": [{ "input": "behavior::interact", "op": "==", "value": true }]
}],
"update_edges": [{ "id": "idle-loop", "speed": 1 }],
"add_inputs": [{ "name": "behavior::interact", "type": "boolean", "default": false, "system": true }]
}'For tiny timing changes, patch one edge directly:
curl -X PATCH https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/edges/idle-to-interact \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "speed": 3 }'Generate Canvas Assets
Generate pending node images and/or transition animations in a single call. Use dry_run: true to preview the planned work, prompts, and credit cost before spending credits. Use targets: "images" for a cheap pose review pass, then targets: "animations" after the poses look right. Reverse animations are free when the graph marks the return edge as a reverse of the forward edge.
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/generate-all \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dry_run": true,
"targets": "animations",
"duration": 4,
"skip_completed": true
}'The response includes planned_jobs, generated, skipped_items, reverse_free, already_complete, estimated_cost, would_charge_credits, and actual_cost so you can see exactly what will queue or what queued and what was skipped.
Reverse transitions are free (0 credits). When generating a transition from A to B with a reverse edge that references it, the B to A animation is created automatically at no additional cost.
Check Progress
Canvas generation progress is returned as the status field on the canvas detail response. Poll GET /v1/collections/:id/canvases/:canvasId and read status to see node and edge counts.
Use status.media as the main readiness interface:
status.media.generation.readymeans node images and parent edge videos are complete.status.media.preview.readymeans the canvas editor can play every concrete edge using base WebM and HEVC derivatives.status.media.preview.waiting_edgesmeans parent videos exist but the original generation job is still preparing derivatives; wait and poll again.status.media.preview.repairablemeans parent videos exist, no source job is still preparing derivatives, and missing base preview derivatives can be repaired safely.status.media.variantsreports optimized size variants that exist or are incomplete; preview repair does not create size variants.
status.ready, status.generated_ready, and status.preview_ready are kept for backward compatibility.
curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY"
# Response:
# {
# "data": {
# "id": "...",
# "graph": { ... },
# "status": {
# "nodes": { "total": 4, "completed": 4, "pending": 0 },
# "edges": { "total": 12, "completed": 8, "pending": 3, "failed": 1 },
# "ready": false,
# "generated_ready": false,
# "preview_ready": false,
# "media": {
# "generation": {
# "ready": false,
# "nodes": { "total": 4, "completed": 4, "pending": 0 },
# "edges": { "total": 12, "completed": 8, "pending": 3, "failed": 1 }
# },
# "preview": {
# "ready": false,
# "repairable": false,
# "repairable_edges": 0,
# "waiting_edges": 0,
# "missing_edges": 4,
# "missing_formats": ["webm", "hevc"]
# },
# "variants": { "sizes": [], "missing": [] }
# },
# "failed_edges": []
# }
# }
# }If status.media.preview.waiting_edges is greater than zero, wait and poll the canvas again. If status.media.preview.repairable is true, repair missing base derivatives with a dry-run first. Inspect status.edge_media[].base.missing only when you need the exact edge-level missing formats.
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/repair \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": "video_derivatives",
"dry_run": true
}'Only call again with "dry_run": false after checking the returned repairs, in_flight, and skipped lists. This repair does not regenerate animations, does not run collection-wide generation, does not create size variants, and does not archive existing assets.
Export
Export the canvas as a MaskoAnimationConfig JSON object ready to use with the Masko embed player. All asset URLs point to the CDN.
curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/export \
-H "Authorization: Bearer masko_YOUR_API_KEY"Built-in Templates
These templates are available out of the box. Use the template ID as template_id when calling POST /v1/collections/:id/canvases. Some built-in templates are compatibility scaffolds for older Claude Code style graphs; new desktop app-control graphs should prefer the behavior::*, action::*, and node::* contract.
Custom Templates
You can save your own templates from an existing canvas or from a raw JSON definition, then apply them to any collection. This lets you reuse state machine graphs across projects.
curl -X POST https://api.masko.ai/v1/canvas-templates \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Custom Template",
"description": "2-state widget with a shared attention behavior",
"template": {
"nodes": [
{ "key": "idle", "name": "Idle", "imagePrompt": "standing relaxed", "position": { "x": 0, "y": 0 } },
{ "key": "active", "name": "Active", "imagePrompt": "alert and ready", "position": { "x": 300, "y": 0 } }
],
"edges": [
{
"source": "idle", "target": "active",
"duration": 4, "description": "becoming alert",
"conditions": [{ "input": "behavior::attention", "op": "==", "value": true }]
}
],
"inputs": [
{ "name": "behavior::attention", "type": "boolean", "default": false, "system": true }
]
}
}'The response includes the template ID. Use it with template_id in the canvas create call to apply it to a new canvas:
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "My Canvas", "template_id": "TEMPLATE_ID" }'See Canvas Templates for the full reference including node/edge overrides and listing templates.