Canvas templates
A template is a ready-made canvas graph (poses with image prompts, transitions with conditions and durations, and inputs) with no media. Copy it into a canvas, then generate the media for your mascot.
List templates
Returns built-in and saved templates. Filter with source=builtin or source=mine.
# All templates (built-in and yours)
curl https://api.masko.ai/v1/canvas-templates \
-H "Authorization: Bearer $MASKO_API_KEY"
# Only built-in templates
curl "https://api.masko.ai/v1/canvas-templates?source=builtin" \
-H "Authorization: Bearer $MASKO_API_KEY"Built-in templates are written for a coding-agent companion, with inputs such as claudeCode::isWorking; pick one that matches your app's states.
Apply a template to a canvas
Create an empty canvas with POST /v1/canvases, then save the template's graph with PATCH /v1/canvases/:canvasId, which is free. Nodes keep their imagePrompt, which generate-all uses to draw the poses before animating the transitions.
const api = 'https://api.masko.ai/v1';
const headers = {
Authorization: `Bearer ${process.env.MASKO_API_KEY}`,
'Content-Type': 'application/json'
};
async function call(path, init = {}) {
const res = await fetch(`${api}${path}`, { headers, ...init });
const body = await res.json();
if (!res.ok) throw new Error(body.error?.message || `HTTP ${res.status}`);
return body.data;
}
// 1. Read the template.
const template = await call('/canvas-templates/claude-code-4state');
// 2. Create an empty canvas.
const canvas = await call('/canvases', {
method: 'POST',
body: JSON.stringify({
project_id: 'PROJECT_ID',
mascot_id: 'MASCOT_ID',
name: 'Main canvas'
})
});
// 3. Turn the template into a canvas graph.
const edgeId = (edge) => edge.key ?? `${edge.source}->${edge.target}`;
const forwardOf = (edge) =>
template.edges.find(
(e) => !e.reverse && e.source === edge.target && e.target === edge.source
);
const graph = {
nodes: template.nodes.map((node) => ({
id: node.key,
itemName: node.name,
imagePrompt: node.imagePrompt,
x: node.position?.x ?? node.x ?? 0,
y: node.position?.y ?? node.y ?? 0
})),
edges: template.edges.map(({ key, reverseOfEdgeKey, reverseOfEdge, ...edge }) => ({
...edge,
// Any State (source "*") routing edges have no video of their own.
duration: edge.duration ?? 0,
id: key ?? `${edge.source}->${edge.target}`,
reverseOfEdgeId: edge.reverse
? (reverseOfEdgeKey ?? reverseOfEdge ?? edgeId(forwardOf(edge)))
: null
})),
inputs: template.inputs ?? [],
viewport: { x: 0, y: 0, zoom: 0.8 }
};
// 4. Save it into the canvas.
await call(`/canvases/${canvas.id}`, {
method: 'PATCH',
body: JSON.stringify({ graph, expected_graph_hash: canvas.graph_content_hash })
});To customize before generating, edit graph before step 4: a node's itemName or imagePrompt, or an edge's description (its animation prompt).
Then dry-run POST /v1/canvases/CANVAS_ID/generate-all with animation_model: "standard" and duration: 5. Each pose image costs 1 credit and each transition 2 credits per second; reverse edges are free. Submit the approved plan as in generate canvas assets.
Save your own
Save from one of your canvases or from a raw graph. public: true shares it with other users.
curl -X POST https://api.masko.ai/v1/canvas-templates \
-H "Authorization: Bearer $MASKO_API_KEY" \
-H "Masko-API-Version: 2026-09-26" \
-H "Content-Type: application/json" \
-d '{
"name": "My widget template",
"description": "3-state sidebar widget with shared behavior inputs",
"canvas_id": "CANVAS_ID",
"mascot_id": "MASCOT_ID",
"public": false
}'/v1/canvas-templates is shared by both API versions. When you send mascot_id, also send Masko-API-Version: 2026-09-26, as in the first example.
Update a template
Change a saved template's name, description or visibility. Built-in templates are read-only.
curl -X PATCH https://api.masko.ai/v1/canvas-templates/tmpl_abc123 \
-H "Authorization: Bearer $MASKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My widget v2",
"description": "Updated description",
"public": true
}'