# Masko API - Complete Documentation Base URL: https://api.masko.ai/v1 Auth: Authorization: Bearer masko_YOUR_API_KEY OpenAPI spec: https://api.masko.ai/v1/openapi.json --- ---url: /docs/quickstart--- # Quickstart %% animation https://assets.masko.ai/7fced6/spark-4735/waving-hello-54937ffb-360.webm https://assets.masko.ai/7fced6/spark-4735/waving-hello-0bf2a5b3-360.mov %% Create your first mascot and get CDN URLs in 5 minutes. Using an AI coding agent? The [CLI](/docs/ai-agents#cli) or [MCP server](/docs/ai-agents#mcp-server) may be faster than raw API calls. Prerequisites: You need an API key. Create one in the [Developer dashboard](https://app.masko.ai/settings/developer). ## Step 1: Create a Project Projects group related collections together. Create one to get started. ```bash curl -X POST https://api.masko.ai/v1/projects \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My App" }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/projects', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'My App' }), }); const project = await res.json(); ``` ## Step 2: Create a Collection A collection represents a single mascot character. Give it a name and a description of the character you want. ```bash curl -X POST https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Felix the Fox", "project_id": "PROJECT_ID", "description": "A friendly orange fox mascot wearing a blue scarf" }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/collections', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Felix the Fox', project_id: 'PROJECT_ID', description: 'A friendly orange fox mascot wearing a blue scarf', }), }); const collection = await res.json(); // Save collection.id for the next steps ``` ## Step 3: Generate an Animation Request a generation by specifying the type and a prompt. The API returns a job ID you can poll for status. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "prompt": "waving hello", "duration": 3 }' ``` ```javascript const res = await fetch( 'https://api.masko.ai/v1/collections/COLLECTION_ID/generate', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'animation', prompt: 'waving hello', duration: 3, }), } ); const { job_id } = await res.json(); ``` ## Step 4: Check the Result Poll the job endpoint until the status is `completed`. The response includes download URLs for all generated assets. ```bash curl https://api.masko.ai/v1/jobs/JOB_ID \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "id": "JOB_ID", # "status": "completed", # "assets": [ # { "type": "video", "url": "https://..." }, # { "type": "webm", "url": "https://..." } # ] # } ``` ```javascript async function pollJob(jobId) { while (true) { const res = await fetch( `https://api.masko.ai/v1/jobs/${jobId}`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', }, } ); const job = await res.json(); if (job.status === 'completed') { return job.assets; } if (job.status === 'failed') { throw new Error(job.error); } // Wait 2 seconds before polling again await new Promise((r) => setTimeout(r, 2000)); } } const assets = await pollJob(job_id); console.log(assets); ``` Once your assets are published to the CDN, the URLs are permanent and served from `assets.masko.ai` with global edge caching. ## CLI Alternative The same flow with the Masko CLI (great for AI agents): ```bash npx @masko/cli login masko projects create --name "My App" masko mascots create --project PROJECT_ID --prompt "A friendly orange fox mascot wearing a blue scarf" masko generate --mascot MASCOT_ID --type animation --prompt "waving hello" --duration 3 --wait ``` ## Next Steps - [Generation](/docs/generate/images) - Deep dive into image, animation, and logo generation options. - [Canvas](/docs/canvas/build) - Build interactive state machines with your mascot. - [AI Agents](/docs/ai-agents) - Set up MCP, CLI, or the Claude Code skill. --- ---url: /docs/authentication--- # Authentication %% animation https://assets.masko.ai/7fced6/spark-4735/holding-key-edbf1ecd-360.webm https://assets.masko.ai/7fced6/spark-4735/holding-key-4b043910-360.mov %% All API requests require a Bearer token in the `Authorization` header. Unauthenticated requests return a `401` error. ## API Keys API keys follow the format `masko_{64-hex-characters}`. When you create a key, only the SHA-256 hash is stored on our servers - the raw key is shown once and cannot be retrieved later. ```bash curl https://api.masko.ai/v1/credits \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const res = await fetch('https://api.masko.ai/v1/credits', { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', }, }); const { credits } = await res.json(); ``` Keep your API key secret. Do not expose it in client-side code or public repositories. If a key is compromised, revoke it immediately from the dashboard. ## Creating API Keys Create API keys from the [Developer dashboard](/api-keys) or programmatically. Each account can have up to 10 active keys per workspace. ### Personal vs Organization Keys API keys are scoped to a workspace: - **Personal keys** access your personal projects and deduct from your personal credits - **Organization keys** access the team's projects and deduct from the team's credit pool When you switch workspaces in the dashboard, the Developer page shows keys for that workspace. Organization keys can be created by admins and owners. Any team member can use an org key. ## Rate Limits The API enforces rate limits to ensure fair usage. If you exceed the limits, requests return a `429 Too Many Requests` response. - **60 requests per minute** per API key - **1,000 requests per hour** per API key Rate limit headers are included in every response: `X-RateLimit-Remaining` and `X-RateLimit-Reset`. If you need higher rate limits for production use, reach out to [paul@masko.ai](mailto:paul@masko.ai). ## Credits Each generation consumes credits from your account balance. Check your balance at any time via the `/v1/credits` endpoint. | Type | Cost | | --- | --- | | Image | 1 credit | | Animation | 5 credits per second | | Logo | 5 credits | | Edit | 1 credit | ## Error Codes The API uses standard HTTP status codes for error responses. | Code | Meaning | | --- | --- | | `401` | Invalid or missing API key | | `402` | Insufficient credits - top up your balance to continue | | `403` | Access denied - you do not own this resource | | `429` | Rate limit exceeded - wait and retry | | `500` | Internal server error - contact support if persistent | --- ---url: /docs/ai-agents--- # AI Agent Integration Add AI mascots to your app without leaving your coding agent. Masko works with Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Goose, Kiro, and more. ## Choose Your Integration | Method | Best For | Setup | |--------|----------|-------| | [**MCP Server**](/docs/ai-tools/mcp) | Native tool calls in your agent | 30 seconds | | [**CLI**](/docs/ai-tools/cli) | Terminal commands, any agent with bash | 1 minute | | [**AI Skills**](/docs/ai-tools/skills) | Teach your agent Masko workflows | 2 minutes | | **Direct API** | Custom HTTP integrations | [Quickstart](/docs/quickstart) | ## Quick Setup The fastest way to get started depends on your tool: ```bash claude mcp add --transport http masko "https://masko.ai/api/mcp" claude /mcp # authenticate ``` ```bash # Add MCP server to your config (see MCP Server page for exact format) # Then install the AI skill for best results ``` ```bash npx @masko/cli login masko generate --mascot ID --type animation --prompt "waving" --duration 4 --wait ``` ## What You Can Do - **Create mascots** from text descriptions in 38 art styles - **Generate images** of any pose or action (1 credit) - **Animate from scratch** - no need to create an image first (5 credits/sec) - **Edit images** - add accessories, change colors, seasonal variations (1 credit) - **Build canvases** - interactive state machines with transitions between poses - **Get instant CDN URLs** - embed in code immediately, they work as placeholders ## Direct API For agents that prefer raw HTTP calls: | Resource | URL | |----------|-----| | LLM-friendly docs | [masko.ai/llms-full.txt](https://masko.ai/llms-full.txt) | | Doc search | `GET /api/v1/docs?q=animation` | | OpenAPI spec | [masko.ai/api/v1/openapi.json](https://masko.ai/api/v1/openapi.json) | In the API, mascots are called "collections" (`POST /v1/collections`). The CLI and MCP use "mascot" but call the same endpoints. --- ---url: /docs/ai-tools/mcp--- # Masko MCP Server The Masko MCP server gives your AI coding agent native tools to create mascots, generate images, animate characters, and build interactive canvases. Server URL: `https://masko.ai/api/mcp` ## Install Configure your client to connect with Masko. %% clients %% --- Claude Code --- Add the MCP server to your project config using the command line: ```bash claude mcp add --scope project --transport http masko "https://masko.ai/api/mcp" ``` Alternatively, add this configuration to `.mcp.json`: ```json { "mcpServers": { "masko": { "type": "http", "url": "https://masko.ai/api/mcp" } } } ``` After configuring the MCP server, you need to authenticate. In a regular terminal (not the IDE extension) run: ```bash claude /mcp ``` Select the **masko** server, then **Authenticate** to begin the authentication flow. --- Cursor --- Add this configuration to `.cursor/mcp.json`: ```json { "mcpServers": { "masko": { "url": "https://masko.ai/api/mcp" } } } ``` After saving, navigate to **Settings > Cursor Settings > Tools & MCP** to verify the server is connected. --- VS Code --- Add this configuration to `.vscode/mcp.json`: ```json { "servers": { "masko": { "type": "http", "url": "https://masko.ai/api/mcp" } } } ``` --- Codex --- Add the Masko MCP server to Codex: ```bash codex mcp add masko --url https://masko.ai/api/mcp ``` Alternatively, add this configuration to `~/.codex/config.toml`: ```toml [mcp_servers.masko] url = "https://masko.ai/api/mcp" ``` After adding the server, enable remote MCP client support by adding this to your `~/.codex/config.toml`: ```toml [features] rmcp_client = true ``` Then authenticate: ```bash codex mcp login masko ``` Run `/mcp` inside Codex to verify authentication. --- Gemini CLI --- Requires Gemini CLI version 0.20.2 or higher. Add the MCP server to Gemini CLI: ```bash gemini mcp add -t http masko https://masko.ai/api/mcp ``` Alternatively, add this configuration to `.gemini/settings.json`: ```json { "mcpServers": { "masko": { "httpUrl": "https://masko.ai/api/mcp" } } } ``` After installation, start the Gemini CLI and run the following command to authenticate: ```bash /mcp auth masko ``` --- Antigravity --- Add this configuration to `~/.gemini/antigravity/mcp_config.json`: ```json { "mcpServers": { "masko": { "serverUrl": "https://masko.ai/api/mcp" } } } ``` After saving the config, restart Antigravity. It will prompt you to complete the OAuth flow to authenticate with Masko. If you run into authentication issues, open the command palette and run **Authentication: Remove Dynamic Authentication Providers** to clear cached credentials. --- Windsurf --- Requires Windsurf version 0.1.37 or higher. Add this configuration to `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "masko": { "command": "npx", "args": [ "-y", "mcp-remote", "https://masko.ai/api/mcp" ] } } } ``` Windsurf does not currently support remote MCP servers over HTTP transport. The `mcp-remote` package acts as a proxy. --- Goose --- Add this configuration to `~/.config/goose/config.yaml`: ```yaml extensions: masko: name: Masko type: streamable_http url: https://masko.ai/api/mcp enabled: true timeout: 300 ``` --- OpenCode --- Add this configuration to `~/.config/opencode/opencode.json`: ```json { "mcp": { "masko": { "type": "remote", "url": "https://masko.ai/api/mcp", "enabled": true } } } ``` After adding the configuration, run the following command to authenticate: ```bash opencode mcp auth masko ``` --- Kiro --- Add this configuration to `.kiro/settings/mcp.json`: ```json { "mcpServers": { "masko": { "url": "https://masko.ai/api/mcp" } } } ``` %% /clients %% ## Authentication Most clients will automatically prompt you to log in during setup. This opens a browser window where you can log in to your Masko account and confirm access. For clients that don't auto-prompt, get your API key from the [Developer dashboard](https://masko.ai/settings/developer) and add it as a header in your config. After authentication, verify the connection by asking your agent: **"Check my Masko credit balance"** - it should call the `get_credits` tool. ## Available Tools Once connected, your agent has 16 tools: ### Mascots | Tool | Description | |------|-------------| | `create_mascot` | Create a new mascot from a text description and art style | | `list_mascots` | List your mascots to find IDs | | `get_mascot` | Get all poses, assets, and CDN URLs for a mascot | ### Generation | Tool | Description | |------|-------------| | `generate` | Generate an image, animation, edit, or logo. Returns CDN URLs instantly | | `generate_batch` | Generate up to 10 items at once | | `get_job` | Check generation status. Supports `wait=true` for long-polling | | `list_jobs` | List recent generation jobs | ### Canvas | Tool | Description | |------|-------------| | `create_canvas` | Create an interactive state machine | | `create_canvas_from_template` | One-step canvas from a pre-built template | | `generate_all_canvas` | Generate all images and animations for a canvas | | `list_canvas_templates` | List available templates | ### Utilities | Tool | Description | |------|-------------| | `get_credits` | Check credit balance before generating | | `list_styles` | List all 38 art styles | | `search_docs` | Search Masko docs by keyword | | `create_project` | Create a project to group mascots | | `list_projects` | List your projects | ## Key Concepts **CDN URLs are instant.** Every `generate` call returns CDN URLs before generation finishes. They serve a placeholder that swaps to the real file automatically. Embed them in code right away. **Animation from scratch.** You don't need to generate an image first. The agent passes both an `image_prompt` (the pose) and `animation_prompt` (the motion) to create everything in one step. **Auto-resolved sources.** When animating an existing item, the agent just passes `item_id`. The server finds the correct source image automatically. **Credits.** Image = 1, animation = 5/sec, edit = 1, logo = 5. Animations loop by default. Background removal and size variants are free. ## Troubleshooting | Issue | Solution | |-------|----------| | Tools not showing up | Restart your editor after adding the MCP server | | Authentication failed | Re-run the auth step for your client (see Install above) | | Token expired | Tokens last 30 days. Re-authenticate to get a new one | | Insufficient credits | Call `get_credits` to check. Top up at masko.ai | | Windsurf not connecting | Ensure `mcp-remote` is installed (`npx -y mcp-remote`) | | Codex not connecting | Add `rmcp_client = true` to `[features]` in config.toml | --- ---url: /docs/ai-tools/cli--- # Masko CLI Generate mascots, images, animations, and interactive canvases from the command line. Works standalone or with any AI agent that can run terminal commands. ## Install ```bash npx @masko/cli login ``` Your browser opens, you log in with your Masko account, and the API key is saved automatically. No key to copy-paste. Or set the key manually: ```bash export MASKO_API_KEY=masko_YOUR_API_KEY ``` Get your API key from the [Developer dashboard](https://masko.ai/settings/developer). ## Quick Start ```bash # Check your credits (image = 1, animation = 5/sec) masko credits # List art styles masko styles # Create a project and mascot masko projects create --name "My App" masko mascots create --project PROJECT_ID --prompt "a friendly pixel art robot" --style pixel # Generate an image masko generate --mascot MASCOT_ID --type image --prompt "waving hello" --wait # Generate an animation from scratch (creates image + animates in one step) masko generate --mascot MASCOT_ID --type animation --prompt "waving happily" --duration 4 --wait # Animate an existing pose (just pass the item ID) masko generate --mascot MASCOT_ID --type animation --item ITEM_ID --prompt "gentle breathing" --duration 4 --wait ``` After generating an image, the CLI prints the exact command to animate it. Just copy and run. ## Commands ### Authentication ```bash masko login # Open browser, log in, key saved automatically ``` ### Mascots ```bash masko mascots create # Create a mascot --project ID # Project ID (required) --prompt TEXT # Character description --style NAME # Art style (run masko styles to see options) masko mascots list # List all mascots --project ID # Filter by project masko mascots get ID # Get mascot details, items, and CDN URLs ``` ### Generation ```bash masko generate # Generate image/animation/edit/logo --mascot ID # Mascot ID (required) --type TYPE # image | animation | edit | logo --prompt TEXT # What to generate (sets both pose + motion for animations) --image-prompt TEXT # Pose description (animation from scratch) --animation-prompt TEXT # Motion description (animation from scratch) --item ID # Animate/edit existing item (auto-resolves source) --duration N # Animation seconds (3-10, default 4) --wait # Wait for completion --json # Raw JSON output --name NAME # Name for the pose (e.g. "wave", "idle") ``` ### Jobs ```bash masko jobs get ID # Check job status --wait # Long-poll until completion (up to 120s) --timeout N # Custom timeout in seconds masko jobs list # List recent jobs --mascot ID # Filter by mascot --status STATUS # pending | processing | completed | failed ``` ### Canvas ```bash masko templates # List available canvas templates masko canvas from-template # Create canvas from a template --mascot ID --template ID masko canvas generate-all # Generate all canvas animations --mascot ID --canvas ID --duration N # Seconds per animation (default 4) ``` ### Utilities ```bash masko credits # Check credit balance masko styles # List all 38 art styles masko projects create --name X # Create a project masko projects list # List projects ``` ## Output Formats By default, the CLI shows human-readable output. Add `--json` for raw JSON (useful for piping or when AI agents parse the output): ```bash # Human-readable masko credits # Total: 14678 # Subscription: 0 # Top-up: 14678 # JSON masko credits --json # {"data":{"subscription":0,"topup":14678,"total":14678}} ``` ## CDN URLs Every `generate` command returns CDN URLs immediately - before the file is ready. These URLs serve a branded placeholder that swaps to the real file automatically when generation completes. ```bash masko generate --mascot ID --type image --prompt "waving" --wait # Output: # CDN URLs (usable immediately): # image: https://assets.masko.ai/abc123/my-mascot/wave-d017.png # transparent_image: https://assets.masko.ai/abc123/my-mascot/wave-1b2c.png ``` Embed these URLs in your code right away. No need to wait. ## Animation Tips **From scratch** (21 credits for 4s): `--prompt` sets both the pose and the motion. Use `--image-prompt` and `--animation-prompt` separately if you want different descriptions. ```bash # Simple (same description for pose and motion) masko generate --mascot ID --type animation --prompt "waving hello" --duration 4 --wait # Specific (different pose vs motion) masko generate --mascot ID --type animation \ --image-prompt "standing with right hand raised" \ --animation-prompt "waving hand back and forth" \ --duration 4 --wait ``` **From existing image** (20 credits, saves 1): Pass `--item` and the source image is auto-resolved. ```bash masko generate --mascot ID --type animation --item ITEM_ID --prompt "breathing gently" --duration 4 --wait ``` Animations loop by default. Pass `--loop false` to disable. ## Credit Costs | Type | Cost | |------|------| | Image | 1 credit | | Animation | 5 credits/second | | Edit | 1 credit | | Logo | 5 credits | Free: background removal, format conversion (webm/hevc), size variants. --- ---url: /docs/ai-tools/skills--- # AI Skills AI skills are instruction files that teach your coding agent how to use Masko. Once installed, your agent knows all the commands, workflows, credit costs, and best practices - no need to explain anything. ## Install for Your Tool ```bash # Copy the skill to your project cp -r packages/skill/masko .claude/skills/masko # Or create CLAUDE.md with Masko instructions at project root ``` ```bash # Create .cursor/rules/masko.mdc # See content below ``` ```bash # Create .github/copilot-instructions.md # Or .github/instructions/masko.instructions.md ``` ```bash # Add to AGENTS.md at project root # Codex also reads CLAUDE.md as fallback ``` ```bash # Add to GEMINI.md at project root ``` ```bash # Add to .windsurfrules at project root # Or .windsurf/rules/masko.md ``` ```bash # Add to .goosehints at project root ``` ```bash # Create .kiro/steering/masko.md ``` ## Skill Content Copy this into the file for your tool. For Claude Code, use the full SKILL.md from `packages/skill/masko/`. For other tools, use this condensed version: ```markdown # Masko - AI Mascot Generation Generate mascots, animations, logos, and interactive canvases via the Masko API. ## Setup MCP (recommended): claude mcp add --transport http masko "https://masko.ai/api/mcp" CLI: npx @masko/cli login API key: https://masko.ai/settings/developer ## CLI Commands masko credits # Check balance masko styles # List 38 art styles masko mascots create --project ID --prompt "description" --style pixel masko generate --mascot ID --type image --prompt "waving hello" --wait masko generate --mascot ID --type animation --prompt "waving" --duration 4 --wait masko generate --mascot ID --type animation --item ITEM_ID --prompt "breathing" --duration 4 --wait masko generate --mascot ID --type edit --item ITEM_ID --prompt "add a santa hat" --wait masko jobs get JOB_ID --wait ## Key Facts - Animation from scratch: --type animation + --prompt creates image + animates in one step - Animate existing: --item ITEM_ID auto-resolves the source image - CDN URLs returned immediately, work as placeholders until ready - Animations loop by default - Credits: image=1, animation=5/sec, edit=1, logo=5 - Free: background removal, format conversion, size variants ## API (direct HTTP) Base URL: https://api.masko.ai/v1 Auth: Authorization: Bearer masko_YOUR_API_KEY Docs: https://masko.ai/llms-full.txt OpenAPI: https://masko.ai/api/v1/openapi.json In the API, mascots are called "collections" (POST /v1/collections). ``` ## Cursor-Specific Format Cursor uses `.mdc` files with YAML frontmatter. Create `.cursor/rules/masko.mdc`: ```markdown --- description: "Use Masko to generate AI mascots, animations, and interactive canvases" alwaysApply: false --- (paste the skill content above) ``` Setting `alwaysApply: false` with a `description` means Cursor's agent will include these rules only when it thinks they're relevant (when you mention mascots, animations, etc.). ## VS Code / Copilot Format For path-specific instructions, create `.github/instructions/masko.instructions.md`: ```markdown --- applyTo: "**/*" --- (paste the skill content above) ``` Or add to `.github/copilot-instructions.md` for always-on instructions. ## Kiro Format Kiro uses a steering directory. Create `.kiro/steering/masko.md`: ```markdown (paste the skill content above) ``` ## What the Skill Teaches Once installed, your agent will know: - **How to create a mascot** from a text description and art style - **How to generate images** with prompts for poses and actions - **How to animate** from scratch or from an existing image - **How to use the CLI** with all flags and options - **Credit costs** so it checks balance before generating - **CDN URL behavior** so it embeds URLs immediately - **Canvas workflows** for interactive state machines - **All 38 art styles** available for generation --- ---url: /docs/how-mascots-work--- # How Mascots Work Understanding the data model behind Masko's API. ## The Hierarchy Every mascot in Masko follows a four-level hierarchy. Projects group your work, collections define characters, items represent poses or actions, and assets are the actual files. ```text Project └── Collection (= one mascot character) ├── Item: "Wave" │ ├── Asset: image (pose.png) │ ├── Asset: transparent_image (pose_nobg.png) │ ├── Asset: video (wave.mp4) │ ├── Asset: webm (wave.webm) │ └── Asset: hevc (wave.mov) ├── Item: "Idle" │ └── ... └── Item: "Thumbs Up" └── ... ``` ## Collections (= Mascots) A collection represents a single mascot character. In the API, mascots are stored as collections with `type: "mascot"`. The CLI and MCP server use the friendlier term "mascot" (`masko mascots create`, `create_mascot`), but they call the same API endpoints (`/v1/collections`). Each collection holds the character's prompt (the text description used for generation), reference images (up to 6 examples of what the character looks like), and a style card (an auto-extracted summary of the character's visual traits). Collections also store settings like animation sizes, CDN configuration, and the caution list used to maintain consistency across generations. ## Items An item is a single pose or action for the mascot - like "Wave", "Idle", or "Thumbs Up". Each item has a name, a prompt describing the action, and a type (`image`, `animation`, or `logo`). When you generate an image for an item, the API combines the collection's character prompt with the item's action prompt to produce a consistent result. ## Assets An asset is a single generated file. Each item can have multiple assets of different types: | Type | Format | Description | | --- | --- | --- | | `image` | .png | Original generated image with background | | `transparent_image` | .png | Background removed, transparent PNG | | `video` | .mp4 | Animated version (H.264) | | `webm` | .webm | Web-optimized format with alpha channel | | `hevc` | .mov | Apple-compatible format with alpha channel | | `stacked_video` | .mp4 | Stacked layout for custom alpha compositing | ## Generation Graph Assets are connected through a generation graph. When you generate an animation, the API first creates an image, then removes the background, then animates it, then converts to web formats. Each step links back to its source via the `generation_links` table. ```text image (.png) └── transparent_image (.png) [role: source] └── video (.mp4) [role: source, end_frame] ├── webm (.webm) [role: source] └── hevc (.mov) [role: source] ``` The `role` field on each link indicates the relationship. `source` means "this asset was derived from that asset". `end_frame` is used for animations where a final pose image guides the motion. ## Reference Images & Style Cards Each collection can have up to 6 reference images. These are examples of what the mascot looks like - they guide every generation to maintain visual consistency. When you first generate an image, Masko automatically extracts a style card from the references. The style card is a structured summary of the character's visual traits (colors, proportions, line style, shading) that gets injected into every prompt. If you change the references, the style card is cleared and re-extracted on the next generation. The caution list accumulates notes from post-generation validation. If a generated image drifts from the style (wrong color, missing detail), the issue is logged and injected into future prompts to prevent recurrence. ## Size Variants Animations can be generated at multiple sizes simultaneously. Set the `animation_sizes` field on a collection to define which resolutions you need (e.g. 512x512, 256x256, 128x128). Resizing is free - you only pay credits for the base animation generation. --- ---url: /docs/credits--- # Credits & Pricing Understanding costs and optimizing your credit usage. ## Credit Costs | Operation | Credits | Notes | | --- | --- | --- | | Image generation | 1 | Per image | | Animation | 5 / second | 4-second animation = 20 credits | | Logo generation | 5 | Per logo | | Image edit | 1 | Inpainting or style transfer | | Reverse (undo) | 0 | Free | ## What's Free These operations cost zero credits: - `analyze-image` - AI analysis of an uploaded image - `analyze-url` - Website analysis and mascot suggestions - `suggest-actions` - AI-suggested poses for a collection - Style card extraction (automatic on first generation) - Background removal (included in generation pipeline) - Format conversion (webm, hevc from video) - Size variants (resizing animations to multiple resolutions) ## How Credits Work Credits are deducted upfront when a generation job starts. If the job fails, credits are automatically refunded to your account. Your account has two credit balances: subscription credits (replenished each billing cycle) and top-up credits (purchased separately, never expire). Subscription credits are used first. ## Checking Your Balance ```bash curl https://api.masko.ai/v1/credits \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const res = await fetch('https://api.masko.ai/v1/credits', { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' } }); const data = await res.json(); console.log(data); ``` ```json { "subscription_credits": 450, "topup_credits": 100, "total": 550 } ``` ## Common Workflow Costs | Workflow | Credits | | --- | --- | | 1 image (with bg removal + formats) | 1 | | 1 animation (4s, includes image + bg removal) | 21 | | 8 poses (images only) | 8 | | Full canvas (4 states, 16 animations at 4s) | ~400 | ## Insufficient Credits If you don't have enough credits, the API returns a `402 Payment Required` response: ```json { "error": "Insufficient credits", "required": 21, "available": 5 } ``` Logo generation requires a Pro plan or higher. If you're on the Free or Starter plan, the API returns a `403 Forbidden` response instead. --- ---url: /docs/create/from-text--- # Create from Text Describe your mascot and pick a style - the API generates the reference image. ## Two Approaches You can either preview multiple variations first (recommended) or create a collection directly in a single call. The preview approach costs 1 credit and gives you 4 options to choose from. Direct creation auto-generates one reference and builds the collection immediately. ## Step 1: Browse Styles GET /v1/styles Fetch the list of available art styles. Each style has a preset ID you can pass to the generation endpoint. ```bash curl https://api.masko.ai/v1/styles \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const res = await fetch('https://api.masko.ai/v1/styles', { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' } }); const { styles } = await res.json(); ``` ```json { "styles": [ { "id": "3d-render", "name": "3D Render", "preview_url": "..." }, { "id": "pixel-art", "name": "Pixel Art", "preview_url": "..." }, { "id": "flat-vector", "name": "Flat Vector", "preview_url": "..." } ] } ``` ## Step 2: Generate Previews POST /v1/generate/preview Generate multiple preview images from your text description. This lets you pick the best variation before committing to a collection. ```bash curl -X POST https://api.masko.ai/v1/generate/preview \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "A friendly orange fox wearing a space helmet", "preset_id": "3d-render", "count": 4 }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/generate/preview', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A friendly orange fox wearing a space helmet', preset_id: '3d-render', count: 4 }) }); const { images } = await res.json(); ``` ```json { "images": [ { "url": "https://api.masko.ai/v1/previews/preview_0.png" }, { "url": "https://api.masko.ai/v1/previews/preview_1.png" }, { "url": "https://api.masko.ai/v1/previews/preview_2.png" }, { "url": "https://api.masko.ai/v1/previews/preview_3.png" } ] } ``` ## Step 3: Create Collection POST /v1/collections Take the preview URL you like best and use it as a reference image to create your collection. ```bash curl -X POST https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Astro Fox", "project_id": "PROJECT_ID", "prompt": "A friendly orange fox wearing a space helmet", "reference_image_urls": [ "https://api.masko.ai/v1/previews/preview_2.png" ] }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/collections', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Astro Fox', project_id: 'PROJECT_ID', prompt: 'A friendly orange fox wearing a space helmet', reference_image_urls: [images[2].url] }) }); const { collection } = await res.json(); console.log(collection.id); // Use this for generation ``` ```json { "collection": { "id": "col_abc123", "name": "Astro Fox", "prompt": "A friendly orange fox wearing a space helmet", "cdn_enabled": true, "created_at": "2026-03-28T10:00:00Z" } } ``` CDN publishing is enabled by default on new collections. You can also set `animation_sizes` at creation time to define which resolutions to generate (e.g. `["512x512", "256x256", "128x128"]`). ## Shortcut: Direct Creation If you don't need to preview, you can create a collection in one call. The API auto-generates a reference image (1 credit) from your prompt. Optionally pass a `style` to apply a visual preset. ```bash curl -X POST https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Astro Fox", "project_id": "PROJECT_ID", "prompt": "A friendly orange fox wearing a space helmet", "style": "3d" }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/collections', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Astro Fox', project_id: 'PROJECT_ID', prompt: 'A friendly orange fox wearing a space helmet', style: '3d' }) }); ``` ## Next Steps - [Generate Images](/docs/generation) - Create poses and actions for your mascot. - [Generate Animations](/docs/generation) - Bring your mascot to life with animated sequences. --- ---url: /docs/create/from-image--- # Create from Image Upload your existing mascot design and let the API auto-detect the character. ## Upload Your Image POST /v1/upload You can upload an image either as a multipart form upload or by providing a URL to an existing image. ### Multipart Upload ```bash curl -X POST https://api.masko.ai/v1/upload \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -F "file=@mascot.png" ``` ```javascript const formData = new FormData(); formData.append('file', fileBlob, 'mascot.png'); const res = await fetch('https://api.masko.ai/v1/upload', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, body: formData }); const { asset_id } = await res.json(); ``` ### URL Upload ```bash curl -X POST https://api.masko.ai/v1/upload \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/my-mascot.png" }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/upload', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com/my-mascot.png' }) }); const { asset_id } = await res.json(); ``` ```json { "asset_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ## Create Collection POST /v1/collections Pass the `asset_id` from the upload step as a reference. You don't need to provide a `prompt` - the image itself is the reference, and the API extracts the character description automatically. If you omit `name`, it's also auto-detected from the image. ```bash curl -X POST https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": "PROJECT_ID", "reference_asset_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"] }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/collections', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: 'PROJECT_ID', reference_asset_ids: [asset_id] }) }); const { collection } = await res.json(); // collection.name is auto-detected from the image // collection.prompt is auto-generated from the visual analysis ``` You can also pass `reference_image_urls` with public URLs instead of uploading first. Both work - use `reference_asset_ids` for uploaded files, `reference_image_urls` for external URLs. ## How Auto-Analysis Works When you create a collection from an image, Masko uses AI vision to analyze the reference and extract a character name and detailed prompt. This analysis is free and happens automatically. The extracted prompt describes the character's visual traits so future generations stay consistent. ## Adding More References POST /v1/collections/:id/references You can add up to 6 reference images to a collection. More references means better consistency across generated poses. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/references \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "asset_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }' ``` ```javascript await fetch(`https://api.masko.ai/v1/collections/${collectionId}/references`, { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ asset_id: assetId // from POST /upload }) }); ``` You can also pass a `url` instead of `asset_id` if you have a public image URL. When references change, the style card is cleared and will be re-extracted on the next generation to reflect the updated visual direction. ## Best Practices - Use 3-4 reference images showing different angles for best consistency - Keep a consistent art style across all references - don't mix 3D and flat vector - Use high-resolution images (1024x1024 or larger recommended) - White or transparent backgrounds work best - the AI focuses on the character, not the scene --- ---url: /docs/create/from-website--- # Create from Website Analyze a website to get AI-generated mascot suggestions based on the brand. ## Analyze a URL POST /v1/analyze-url Send a website URL and the API will screenshot the page, extract brand colors, analyze the visual identity, and suggest mascot concepts that match the brand. This endpoint is free. ```bash curl -X POST https://api.masko.ai/v1/analyze-url \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com" }' ``` ```javascript const res = await fetch('https://api.masko.ai/v1/analyze-url', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com' }) }); const { suggestions } = await res.json(); ``` ```json { "brand": { "name": "Example Corp", "colors": ["#2563eb", "#1e40af", "#f8fafc"], "industry": "Developer Tools" }, "suggestions": [ { "name": "Codey", "prompt": "A friendly blue robot with rounded features...", "style": "3d-render" }, { "name": "Buildy", "prompt": "A cheerful construction worker hamster...", "style": "flat-vector" } ] } ``` ## Use Suggestions Take a suggestion from the response and pass its prompt and style to create a collection. You can use the direct creation shortcut to do it in one call. ```bash curl -X POST https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Codey", "project_id": "PROJECT_ID", "prompt": "A friendly blue robot with rounded features...", "style": "3d" }' ``` ```javascript const suggestion = suggestions[0]; const res = await fetch('https://api.masko.ai/v1/collections', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: suggestion.name, project_id: 'PROJECT_ID', prompt: suggestion.prompt, style: suggestion.style }) }); ``` ## When to Use This Choose the right creation method for your use case: - **From website** - You have a brand but no mascot idea yet. The AI analyzes the site and suggests characters that match the visual identity. - **From text** - You already know what you want. Describe the character, pick a style, and generate previews. - **From image** - You have existing artwork. Upload it and the API auto-detects the character for consistent new poses. --- ---url: /docs/generate/images--- # Images & Poses Generate static mascot images from text prompts. Each image costs 1 credit and produces both a full image and a transparent (background-removed) variant. POST /v1/collections/:id/generate ## Generate an Image Send a `type: "image"` request with a `name` for the item and an `image_prompt` describing the pose. A new item is created in your collection with pending image and transparent_image assets. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "image", "name": "Waving hello", "image_prompt": "waving hello with a friendly smile, one arm raised" }' ``` ```json { "data": { "job_id": "job_abc123", "status": "pending", "type": "image", "item_id": "item_def456", "item_name": "Waving hello", "estimated_cost": 1, "asset_ids": { "image": "ast_img_001", "transparent_image": "ast_img_002" }, "urls": { "image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-a1b2c3.png", "transparent_image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-d4e5f6.png" } }, "poll": "/v1/jobs/job_abc123" } ``` ## Edit an Image Modify an existing image with natural language instructions. Pass `type: "edit"` along with `source_image_asset_id` and `edit_instructions`. The edit creates new assets on the same item. Costs **1 credit**. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "edit", "item_id": "item_def456", "source_image_asset_id": "ast_img_001", "edit_instructions": "Add a party hat and confetti falling around" }' ``` ```json { "data": { "job_id": "job_edit789", "status": "pending", "type": "edit", "item_id": "item_def456", "item_name": "Waving hello", "estimated_cost": 1, "asset_ids": { "image": "ast_img_010", "transparent_image": "ast_img_011" }, "urls": { "image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-g7h8i9.png", "transparent_image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-j0k1l2.png" } }, "poll": "/v1/jobs/job_edit789" } ``` ## Regenerate To regenerate an existing item, pass the same `item_id` with a new `image_prompt`. The endpoint creates fresh assets under the same item each time. Previous assets remain accessible - nothing is overwritten. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "image", "item_id": "item_def456", "image_prompt": "waving hello with both arms raised high" }' ``` ## Style Consistency Masko keeps your mascot looking consistent across all generated images using two mechanisms: - **Reference images** - Up to 6 images pinned as references in your collection. Every generation includes these as visual context so the AI matches the character's appearance. - **Style card** - Automatically extracted from your references on the first generation. The style card captures defining traits like color palette, proportions, and design style, then injects them into every prompt. You do not need to configure either of these manually. Add reference images via the [references endpoint](/docs/reference), and the style card is generated lazily when you first run a generation. If your character looks different across generations, add 2-3 reference images showing the character from different angles. The style card will recalculate automatically when references change. ## Image vs Animation Use **images** when you need a static pose - profile pictures, thumbnails, marketing assets, or any context where motion is not needed. Images cost 1 credit and generate in a few seconds. Use **animations** when you need movement - idle loops, transitions between states, reactions, or interactive behaviors. Animations start at 16 credits (3 seconds) and take longer to generate. See the [Animations guide](/docs/generate/animations) for details. --- ---url: /docs/generate/animations--- # Animations %% animation https://assets.masko.ai/7fced6/spark-4735/painting-7a826736-360.webm https://assets.masko.ai/7fced6/spark-4735/painting-76a264c3-360.mov %% Generate animated mascot videos from scratch or from existing images. All animation types use the same generate endpoint with `type: "animation"`. POST /v1/collections/:id/generate ## Image + Animation (New) Generate a new image and animate it in one request. Provide both an `image_prompt` (for the pose) and an `animation_prompt` (for the motion). This costs 1 credit for the image plus 5 credits per second of video - a 4-second animation costs **21 credits** total. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Idle breathing", "image_prompt": "standing relaxed with arms at sides", "animation_prompt": "gentle breathing motion, subtle body sway", "duration": 4, "loop": true }' ``` ```json { "data": { "job_id": "job_anim_001", "status": "pending", "type": "animation", "item_id": "item_anim_100", "item_name": "Idle breathing", "estimated_cost": 21, "asset_ids": { "image": "ast_img_050", "transparent_image": "ast_img_051", "video": "ast_vid_052", "webm": "ast_vid_053", "hevc": "ast_vid_054" }, "urls": { "image": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-a1b2.png", "transparent_image": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-c3d4.png", "video": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-e5f6.mp4", "webm": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-g7h8.webm", "hevc": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-i9j0.mov" } }, "poll": "/v1/jobs/job_anim_001" } ``` ## Animate Existing Image Animate an image you already have by passing `source_image_asset_id`. This skips image generation, so you only pay for the video - **20 credits** for 4 seconds. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Waving animated", "source_image_asset_id": "ast_img_001", "animation_prompt": "waving hello with smooth arm motion", "duration": 4, "loop": true }' ``` ```json { "data": { "job_id": "job_anim_002", "status": "pending", "type": "animation", "item_id": "item_anim_101", "item_name": "Waving animated", "estimated_cost": 20, "asset_ids": { "video": "ast_vid_060", "webm": "ast_vid_061", "hevc": "ast_vid_062" }, "urls": { "video": "https://assets.masko.ai/u/felix-the-fox/waving-animated-a1b2.mp4", "webm": "https://assets.masko.ai/u/felix-the-fox/waving-animated-c3d4.webm", "hevc": "https://assets.masko.ai/u/felix-the-fox/waving-animated-e5f6.mov" } }, "poll": "/v1/jobs/job_anim_002" } ``` ## Transitions Create a transition between two poses by providing both `source_image_asset_id` and `end_image_asset_id`. Transitions automatically set `loop: false` since they play once between two states. Costs **20 credits** for 4 seconds. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Idle to Wave", "source_image_asset_id": "ast_img_idle", "end_image_asset_id": "ast_img_wave", "animation_prompt": "smoothly transitioning from idle stance to waving", "duration": 4 }' ``` ## Auto-Reverse When creating a transition, set `auto_reverse: true` to automatically generate the return transition (B to A) at **0 extra credits**. The response includes a `reverse_job` with its own job ID and asset IDs. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Idle to Wave", "source_image_asset_id": "ast_img_idle", "end_image_asset_id": "ast_img_wave", "animation_prompt": "transitioning from idle to waving", "auto_reverse": true, "reverse_name": "Wave to Idle", "duration": 4 }' ``` ```json { "data": { "job_id": "job_fwd_001", "status": "pending", "type": "animation", "item_id": "item_fwd_100", "item_name": "Idle to Wave", "estimated_cost": 20, "asset_ids": { "video": "ast_vid_070", "webm": "ast_vid_071", "hevc": "ast_vid_072" }, "urls": { ... }, "reverse_job": { "job_id": "job_rev_002", "item_id": "item_rev_101", "status": "pending", "cost": 0, "asset_ids": { "video": "ast_vid_080", "webm": "ast_vid_081", "hevc": "ast_vid_082" } } }, "poll": "/v1/jobs/job_fwd_001" } ``` Auto-reverse is essential for canvas transitions. When building a state machine, every A-to-B transition needs a matching B-to-A. Use auto_reverse to get both for the price of one. ## Duration & Looping The `duration` field accepts values from **3 to 10 seconds**. Default is 4 seconds. The cost formula is `5 x duration` credits for the video portion. The `loop` field defaults to `true` for standard animations and is automatically set to `false` for transitions (when `end_image_asset_id` is provided). Looping animations seamlessly repeat; non-looping animations play once and hold the last frame. ## Output Formats Every animation produces multiple format variants optimized for different platforms: | Type | Format | Use Case | | --- | --- | --- | | `video` | MP4 (H.264) | Universal fallback, opaque background | | `webm` | WebM (VP9) | Transparent video for Chrome, Firefox, Edge | | `hevc` | MOV (HEVC + Alpha) | Transparent video for Safari, iOS, macOS | | `stacked_video` | MP4 (stacked) | Transparent video for Android (color + alpha stacked vertically) | ## Size Variants By default, animations are generated at full resolution. You can configure a collection to automatically produce smaller size variants for each animation - useful for responsive layouts, thumbnails, or mobile-optimized assets. PATCH /v1/collections/:id/settings ```bash curl -X PATCH https://api.masko.ai/v1/collections/COL_ID/settings \ -H "Authorization: Bearer masko_..." \ -H "Content-Type: application/json" \ -d '{ "publish_params": { "animation_sizes": { "enabled": true, "sizes": [480, 360, 240] } } }' ``` Available sizes are `720`, `480`, `360`, and `240` pixels. When enabled, every new animation automatically generates resized variants alongside the original. Existing animations in the collection are also resized retroactively. ### Instant CDN URLs for Size Variants When generating an animation, pass the `sizes` array to get pre-allocated CDN URLs for specific size variants immediately in the response: ```bash curl -X POST https://api.masko.ai/v1/collections/COL_ID/generate \ -H "Authorization: Bearer masko_..." \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "wave", "image_prompt": "standing with a smile", "animation_prompt": "waving hello", "duration": 3, "sizes": [480] }' ``` The response includes both original and variant URLs: ```json { "urls": { "webm": "https://assets.masko.ai/.../wave.webm", "hevc": "https://assets.masko.ai/.../wave.mov", "webm_480": "https://assets.masko.ai/.../wave-480.webm", "hevc_480": "https://assets.masko.ai/.../wave-480.mov", "stacked_video_480": "https://assets.masko.ai/.../wave-480.mp4" } } ``` The `sizes` parameter is a filter - it only returns URLs for sizes that are enabled in the collection settings. If you request `sizes: [720]` but the collection only has `[480, 360]` configured, no 720 URLs are returned. All variant URLs serve placeholders immediately and are replaced with the real resized files when the size variant workflow completes. Poll the job to check `size_variants.status`. Since `sizes` returns CDN URLs directly in the generate response, you can embed them in your app immediately without waiting for the job to finish or making a second API call. This is the fastest way to wire up responsive animations. ## Cost Reference Summary of animation costs at the default 4-second duration: | Operation | Formula | 4s Cost | | --- | --- | --- | | New image + animation | 1 + (5 x duration) | 21 credits | | Animate existing image | 5 x duration | 20 credits | | Transition (A to B) | 5 x duration | 20 credits | | Auto-reverse (B to A) | Free | 0 credits | --- ---url: /docs/generate/batch--- # Batch Generation & AI Suggestions Generate multiple items in a single API call, and use AI vision to get smart action suggestions for your mascot. ## Batch Generation Send up to **10 generation requests** in a single call. Each item in the `requests` array follows the same format as the single generate endpoint. All requests run in parallel. POST /v1/collections/:id/generate-batch ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate-batch \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "requests": [ { "type": "image", "name": "Waving", "image_prompt": "waving hello with a friendly smile" }, { "type": "image", "name": "Thinking", "image_prompt": "hand on chin, looking up thoughtfully" }, { "type": "animation", "name": "Celebrating", "image_prompt": "jumping with arms raised in celebration", "animation_prompt": "jumping up and down excitedly, confetti motion", "duration": 4, "loop": true } ] }' ``` ```json { "data": { "jobs": [ { "job_id": "job_batch_001", "item_id": "item_b1", "item_name": "Waving", "status": "pending", "estimated_cost": 1, "asset_ids": { "image": "ast_001", "transparent_image": "ast_002" }, "urls": { "image": "https://assets.masko.ai/u/.../waving-a1b2.png", ... } }, { "job_id": "job_batch_002", "item_id": "item_b2", "item_name": "Thinking", "status": "pending", "estimated_cost": 1, "asset_ids": { "image": "ast_003", "transparent_image": "ast_004" }, "urls": { ... } }, { "job_id": "job_batch_003", "item_id": "item_b3", "item_name": "Celebrating", "status": "pending", "estimated_cost": 21, "asset_ids": { "image": "ast_005", "video": "ast_006", "webm": "ast_007", "hevc": "ast_008" }, "urls": { ... } } ], "total_cost": 23 } } ``` ## When to Batch vs Sequential Use **batch** when you know all the items upfront - for example, generating a full set of poses for a new mascot. All items are created and queued in parallel, which is faster than sending individual requests. Use **sequential requests** when each generation depends on the previous result - for example, generating an image first, then animating it with `source_image_asset_id`. You need the first job to complete before starting the second. ## AI Action Suggestions Ask the AI to suggest action poses for your mascot. The endpoint analyzes your mascot's reference images (or the first completed image in the collection) and returns 6-8 action names that suit the character. This is **free** - no credits are deducted. POST /v1/collections/:id/suggest-actions ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/suggest-actions \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json { "data": { "suggestions": [ "Wave Hello", "Thumbs Up", "Thinking", "Celebrate", "Point Right", "Sleeping", "Running", "Confused" ] } } ``` Suggestions automatically exclude items that already exist in the collection, so you can call it repeatedly as you build out your pose library. Use the suggestion names directly as `name` values in a batch generation request. The AI picks names that also work well as image prompts. --- ---url: /docs/generate/cdn--- # CDN URLs & Size Variants Every generation returns CDN URLs immediately, before the assets finish generating. You can embed these URLs in your app right away - they work from the moment you receive them. ## How Instant CDN URLs Work When you call the generate endpoint, Masko uploads a branded placeholder to the CDN path and returns the URL in the response. Your app can start using this URL immediately. Once generation completes, the real asset file replaces the placeholder at the same URL - seamlessly, with no URL change needed on your side. This means you can build your UI, set up image tags, and configure video players before any generation finishes. The placeholder is a lightweight branded image that signals "generating" to users. CDN URLs serve a branded Masko placeholder until generation completes. No broken images, no loading spinners - just a smooth transition from placeholder to final asset. ## URL Format CDN URLs follow this structure: ```text https://assets.masko.ai/{user_prefix}/{collection_slug}/{item_slug}-{hash}.{ext} Example: https://assets.masko.ai/u/felix-the-fox/waving-hello-a1b2c3d4.png ``` - **user_prefix** - Your account prefix (e.g. `u`), set automatically. - **collection_slug** - Derived from the collection name. Can be changed via the cdn-slug endpoint. - **item_slug** - Derived from the item name when created. - **hash** - Short unique hash to prevent collisions. - **ext** - File extension based on asset type (png, mp4, webm, mov). ## Size Variants Configure `animation_sizes` in the collection settings to generate pre-rendered size variants. Size variants are **free** - no extra credits. Size variant URLs append the resolution suffix before the extension: ```text # Original (full resolution) https://assets.masko.ai/u/felix-the-fox/waving-a1b2.webm # 480px variant https://assets.masko.ai/u/felix-the-fox/waving-c3d4-480.webm # 360px variant https://assets.masko.ai/u/felix-the-fox/waving-e5f6-360.webm ``` ### Get variant URLs instantly Pass `sizes` in the generate request to get pre-allocated CDN URLs for specific variants: ```json { "type": "animation", "name": "wave", "animation_prompt": "waving hello", "sizes": [480, 360] } ``` The response `urls` object includes keys like `webm_480`, `hevc_480` alongside the originals. These URLs serve placeholders immediately and swap to real files once the size variant workflow finishes. The `sizes` parameter is a filter on what you get back - it only returns URLs for sizes that are enabled in the collection's `animation_sizes` config. Poll the job's `size_variants.status` field to know when all variants are ready. ## Check CDN Status Get the CDN publishing status for all assets in a collection. Shows which assets have been published, their file sizes, and current status. GET /v1/collections/:id/cdn-status ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/cdn-status \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```json { "data": { "assets": [ { "asset_id": "ast_img_001", "item_name": "Waving hello", "type": "image", "cdn_url": "https://assets.masko.ai/u/felix-the-fox/waving-hello-a1b2.png", "status": "completed", "file_size": 245832 }, { "asset_id": "ast_vid_010", "item_name": "Idle breathing", "type": "webm", "cdn_url": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-c3d4.webm", "status": "completed", "file_size": 1548200 } ] } } ``` ## Get All URLs Retrieve all URLs for every item in a collection, organized by item. Returns CDN URLs when available, falling back to signed storage URLs. GET /v1/collections/:id/urls ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/urls \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```json { "data": { "collection": "Felix the Fox", "items": [ { "name": "Waving hello", "image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-a1b2.png", "transparent_image": "https://assets.masko.ai/u/felix-the-fox/waving-hello-c3d4.png" }, { "name": "Idle breathing", "image": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-e5f6.png", "transparent_image": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-g7h8.png", "animations": [ { "video": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-i9j0.mp4", "transparent_video_webm": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-k1l2.webm", "transparent_video_mov": "https://assets.masko.ai/u/felix-the-fox/idle-breathing-m3n4.mov" } ] } ] } } ``` ## Change Slug Update the collection's CDN slug. This changes the URL path for all future assets. Existing CDN URLs are not affected - only new publishes use the new slug. PATCH /v1/collections/:id/cdn-slug ```bash curl -X PATCH https://api.masko.ai/v1/collections/COLLECTION_ID/cdn-slug \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "slug": "felix" }' ``` ```json { "data": { "slug": "felix" } } ``` Slugs must be 2-50 characters, lowercase alphanumeric with dashes. Each slug must be unique across all collections. ## Disabling CDN If you do not need CDN URLs, set `cdn_enabled: false` in the collection settings when creating or updating the collection. This skips placeholder uploads and CDN URL allocation. Assets are still generated and accessible via signed storage URLs through the jobs endpoint. --- ---url: /docs/generation--- # Generation %% animation https://assets.masko.ai/7fced6/spark-4735/painting-dddca74c-360.webm https://assets.masko.ai/7fced6/spark-4735/painting-bba26b7f-360.mov %% A single unified endpoint handles all generation types: images, animations, logos, and edits. Each request creates a job you can poll for results. POST /v1/collections/:id/generate ## Image Generation Generate a mascot image from a text prompt. Costs **1 credit**. Returns both a full image and a transparent (background-removed) variant. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "image", "name": "Waving hello", "image_prompt": "waving hello with a friendly smile" }' ``` ```javascript const res = await fetch( 'https://api.masko.ai/v1/collections/COLLECTION_ID/generate', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'image', name: 'Waving hello', image_prompt: 'waving hello with a friendly smile', }), } ); const { job_id, asset_ids, urls } = await res.json(); ``` Response includes `asset_ids.image` and `asset_ids.transparent_image` for the two variants. ## Animation Generation Generate an animated mascot from scratch. Costs **21 credits** for a 4-second animation (1 credit for the image + 5 credits/sec for the video). Returns image, video, webm, and hevc assets. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Dancing", "image_prompt": "standing in a dance pose, arms raised", "animation_prompt": "dancing energetically with bouncy movements", "duration": 4, "loop": true }' ``` ```javascript const res = await fetch( 'https://api.masko.ai/v1/collections/COLLECTION_ID/generate', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'animation', name: 'Dancing', image_prompt: 'standing in a dance pose, arms raised', animation_prompt: 'dancing energetically with bouncy movements', duration: 4, loop: true, }), } ); const { job_id, asset_ids, urls } = await res.json(); // asset_ids: { image, transparent_image, video, webm, hevc } ``` ### Size Variants Pass a `sizes` array to get pre-allocated CDN URLs for smaller variants directly in the response. Available sizes: `720`, `480`, `360`, `240`. Requires [size variants enabled](/docs/generate/animations#size-variants) on the collection. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Dancing", "image_prompt": "standing in a dance pose", "animation_prompt": "dancing energetically", "duration": 4, "sizes": [360] }' ``` The response includes both original and variant URLs (`webm_360`, `hevc_360`, `stacked_video_360`). These URLs work immediately as placeholders and switch to the real resized files once processing completes. ## Animate Existing Image Animate an image you already have. Pass `source_image_asset_id` to skip image generation. Costs **20 credits** for 4 seconds (video only, no image cost). ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Waving animated", "source_image_asset_id": "IMAGE_ASSET_ID", "animation_prompt": "waving hello smoothly", "duration": 4 }' ``` ## Transitions Create a transition between two poses by providing both a source and end image. Set `auto_reverse` to automatically generate the return transition at no extra cost. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "animation", "name": "Idle to Wave", "source_image_asset_id": "IDLE_IMAGE_ID", "end_image_asset_id": "WAVE_IMAGE_ID", "animation_prompt": "transitioning from idle to waving", "auto_reverse": true, "reverse_name": "Wave to Idle" }' ``` When `auto_reverse` is true, the response includes a `reverse_job` object with its own job ID and asset IDs. The reverse costs 0 credits. ## Edit Image Modify an existing image with natural language instructions. Costs **1 credit**. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "edit", "item_id": "EXISTING_ITEM_ID", "source_image_asset_id": "IMAGE_ASSET_ID", "edit_instructions": "Add a santa hat and snow falling in the background" }' ``` ## Logo Pack Generate a full logo pack with multiple style variants. Costs **5 credits**. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "logo", "name": "App Logo", "logo_description": "Iconic representation of the character face", "logo_style_name": "Flat", "logo_style_instruction": "Flat design with solid colors, no gradients or shadows" }' ``` ## Batch Generation Generate multiple items in a single request. Each item in the batch follows the same format as the single generate endpoint. POST /v1/collections/:id/generate-batch ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/generate-batch \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "type": "image", "name": "Waving", "image_prompt": "waving hello" }, { "type": "image", "name": "Thinking", "image_prompt": "thinking with hand on chin" }, { "type": "animation", "name": "Dancing", "image_prompt": "dancing pose", "animation_prompt": "dancing", "duration": 4 } ] }' ``` ## Preview Test a generation prompt without creating items or spending credits. Returns a temporary preview image URL. POST /v1/generate/preview ```bash curl -X POST https://api.masko.ai/v1/generate/preview \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "collection_id": "COLLECTION_ID", "prompt": "a friendly fox mascot waving hello" }' ``` ## Polling Jobs Every generation returns a `job_id`. Poll the job endpoint to check progress. Use `?wait=true` for long polling - the server holds the connection until the job completes or times out. GET /v1/jobs/:id ```bash # Standard polling curl https://api.masko.ai/v1/jobs/JOB_ID \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Long polling (waits for completion) curl https://api.masko.ai/v1/jobs/JOB_ID?wait=true \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript // Long polling - simplest approach const res = await fetch( `https://api.masko.ai/v1/jobs/${jobId}?wait=true`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const job = await res.json(); if (job.status === 'completed') { console.log('Assets:', job.assets); } ``` CDN URLs are returned immediately in the generate response, before generation finishes. You can embed these URLs in your app right away - they serve a placeholder until the real asset is ready, then automatically switch over. For animations, pass `sizes: [360]` to also get instant CDN URLs for smaller variants - no extra API call needed. --- ---url: /docs/canvas--- # Canvas & Templates A canvas is an interactive state machine for your mascot. Define poses (nodes), transitions (edges), conditions, and input triggers to create mascots that react to user actions in real time. ## Concepts - [Nodes (Poses)] - Each node represents a mascot pose or state - idle, waving, thinking, etc. Nodes reference an animation asset. - [Edges (Transitions)] - Edges connect nodes and define how the mascot moves between poses. Each edge is an animation from one pose to another. - [Loops] - Looping animations play continuously within a pose. Non-looping animations play once then hold the last frame. - [Conditions & Inputs] - Edges can have conditions like 'on hover', 'on click', or custom triggers. Inputs let your app control the mascot programmatically. ## Create a Canvas Create a canvas within a collection. A canvas starts empty - you add nodes and edges by applying a template or building manually. POST /v1/collections/:id/canvases ```bash 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" }' ``` ```javascript const res = await fetch( 'https://api.masko.ai/v1/collections/COLLECTION_ID/canvases', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Main Canvas' }), } ); const canvas = await res.json(); // canvas.id is used in subsequent requests ``` ## Apply a Template Templates define a pre-built set of nodes and edges. Applying a template populates the canvas with poses and transitions, then generates all the required images and animations. POST /v1/collections/:id/canvases/:canvasId/from-template ```bash # Apply the Claude Code 4-state template curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/from-template \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template_id": "claude-code-4state" }' ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/from-template`, { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ template_id: 'claude-code-4state', }), } ); const result = await res.json(); // result.nodes, result.edges, result.jobs ``` ## Generate All Animations Once your canvas has nodes with images, generate all the transition animations between connected poses in a single call. Transitions use `auto_reverse` by default, so reverse animations are generated for free. POST /v1/collections/:id/canvases/:canvasId/generate-all ```bash 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" ``` Reverse transitions are free (0 credits). When generating a transition from A to B with `auto_reverse`, the B to A animation is created automatically at no additional cost. ## Check Progress Get the generation status of all nodes and edges in a canvas. Returns completion percentages and individual asset statuses. GET /v1/collections/:id/canvases/:canvasId/status ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/status \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "canvas_id": "...", # "total_jobs": 12, # "completed": 8, # "pending": 3, # "failed": 1, # "progress": 0.67 # } ``` ## Export Export the canvas as a `MaskoAnimationConfig` JSON object ready to use with the Masko embed player. All asset URLs point to the CDN. GET /v1/collections/:id/canvases/:canvasId/export ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/export \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/export`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const config = await res.json(); // Pass config to your Masko embed player // ``` ## Built-in Templates These templates are available out of the box. Use the template ID when calling the `from-template` endpoint. - [claude-code-4state] - 4 poses (idle, thinking, talking, celebrating) with 16 transitions at 4 seconds each. Full interactive mascot - approximately 400 credits. - [claude-code-4state-lite] - Same 4 poses with shorter transitions for a lighter-weight mascot. Lower credit cost with faster generation times. ## 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. ```bash 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": "3-state widget with hover and click", "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": "clicked", "op": "==", "value": true }] } ], "inputs": [ { "name": "agent::isWorking", "type": "boolean", "default": false, "system": true } ] } }' ``` ```bash 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 Canvas Template", "description": "Saved from an existing canvas", "canvas_id": "CANVAS_ID", "collection_id": "COLLECTION_ID" }' ``` The response includes the template ID. Use it with `from-template` to apply it to any canvas: ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/from-template \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template_id": "TEMPLATE_ID" }' ``` See [Canvas Templates](/docs/canvas/templates) for the full reference including node/edge overrides and listing templates. --- ---url: /docs/canvas/build--- # Build a Canvas Create an interactive state machine with poses, transitions, and conditions. A canvas defines how your mascot behaves and reacts to user interactions in real time. ## What is a Canvas? A canvas is a state machine where each state is a mascot pose (an image or animation) and transitions are animated videos that play when moving between states. You define conditions and inputs that trigger transitions - like hover, click, or custom events from your app. The Masko embed player reads the canvas configuration and handles all the playback logic. ## Nodes & Edges **Nodes** represent poses or states. Each node references an image asset ID and has a position on the canvas editor grid. The `itemName` is the human-readable label. **Edges** are transitions between nodes. Each edge connects a `source` node to a `target` node and can have: - **conditions** - Rules that must be met to trigger the transition (see below). - **priority** - When multiple edges can fire, the highest priority wins. Higher number = higher priority. - **speed** - Playback speed multiplier (e.g. 1.5 for faster, 0.5 for slower). Default is 1. - **duration** - Video duration in seconds. Used for generation. - **description** - Animation prompt used when generating the transition video. - **reverse** - If true, this edge plays the reverse of another edge's video instead of generating a new one. Use `source: "*"` (any state) for edges that can fire from any node - useful for global triggers like an error state or reset. ## Conditions & Inputs Each edge can have an array of `conditions`. A condition has three fields: - **input** - The name of the input to check (e.g. `"isHovered"`, `"clickCount"`). - **op** - The comparison operator: `"eq"`, `"neq"`, `"gt"`, `"lt"`, `"gte"`, `"lte"`. - **value** - The value to compare against. The canvas supports these input types: - **boolean** - True/false values. Example: `isHovered`, `isActive`. - **number** - Numeric values. Example: `clickCount`, `scrollPosition`. - **trigger** - Fire-once events. The value resets after the transition fires. Example: `onClick`. - **string** - Text values. Example: `currentPage`, `userRole`. ## Create a Canvas Create a canvas with an inline graph definition. The graph contains nodes, edges, viewport settings, and input declarations. POST /v1/collections/:id/canvases ```bash 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", "graph": { "nodes": [ { "id": "ast_img_idle", "x": 0, "y": 0, "itemName": "Idle" }, { "id": "ast_img_wave", "x": 300, "y": 0, "itemName": "Waving" } ], "edges": [ { "id": "edge_001", "source": "ast_img_idle", "target": "ast_img_wave", "duration": 4, "description": "transitioning from idle to waving", "conditions": [ { "input": "isHovered", "op": "eq", "value": true } ] }, { "id": "edge_002", "source": "ast_img_wave", "target": "ast_img_idle", "duration": 4, "description": "returning from wave to idle", "conditions": [ { "input": "isHovered", "op": "eq", "value": false } ], "reverse": true, "reverseOfEdgeId": "edge_001" } ], "viewport": { "x": 0, "y": 0, "zoom": 0.8 }, "inputs": [ { "name": "isHovered", "type": "boolean", "defaultValue": false } ] } }' ``` ## Update the Graph Update a canvas by sending a full replacement graph via PUT. The entire graph (nodes, edges, inputs) is replaced - there is no partial update. PUT /v1/collections/:id/canvases/:canvasId ## Example: Sleep/Wake Canvas A complete two-state canvas where the mascot sleeps by default and wakes up when clicked. Uses a trigger input that fires once per click. ```bash 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": "Sleep Wake", "graph": { "nodes": [ { "id": "ast_sleeping", "x": 0, "y": 0, "itemName": "Sleeping" }, { "id": "ast_awake", "x": 400, "y": 0, "itemName": "Awake" } ], "edges": [ { "id": "edge_wake", "source": "ast_sleeping", "target": "ast_awake", "duration": 3, "description": "waking up with a stretch and yawn", "conditions": [ { "input": "onClick", "op": "eq", "value": true } ], "priority": 10 }, { "id": "edge_sleep", "source": "ast_awake", "target": "ast_sleeping", "duration": 4, "description": "slowly falling asleep, eyes drooping", "conditions": [ { "input": "onClick", "op": "eq", "value": true } ], "priority": 10 } ], "viewport": { "x": 0, "y": 0, "zoom": 0.8 }, "inputs": [ { "name": "onClick", "type": "trigger", "defaultValue": false } ] } }' ``` ```json { "data": { "id": "canvas_abc123", "name": "Sleep Wake", "collection_id": "COLLECTION_ID", "graph": { "nodes": [...], "edges": [...], "viewport": { "x": 0, "y": 0, "zoom": 0.8 }, "inputs": [ { "name": "onClick", "type": "trigger", "defaultValue": false } ] }, "created_at": "2026-03-28T10:00:00Z", "updated_at": "2026-03-28T10:00:00Z" } } ``` After creating a canvas with a graph, use the `generate-all` endpoint to generate all transition animations at once. Reverse transitions are created for free with auto_reverse. --- ---url: /docs/canvas/templates--- # Canvas Templates Templates are blueprints for canvases. They define the nodes (poses), edges (transitions), conditions, and prompts - but no generated assets. When you apply a template, Masko creates items and kicks off image generation for every node. ## What Templates Provide A template contains the full graph structure: node positions, names, image prompts, edge connections, conditions, durations, and input definitions. It does not contain any generated assets. When applied, the template creates new items in the collection, generates images for each node, and saves the graph to the canvas. You then use `generate-all` to create all the transition animations. ## Built-in Templates Two templates are available out of the box. Use the template ID when calling the `from-template` endpoint. - [claude-code-4state]() - 4 poses (idle, thinking, talking, celebrating) with 16 transitions at 4 seconds each. Full interactive mascot with all directions covered. Approximately 288 credits for images + animations. - [claude-code-4state-lite]() - Same 4 poses with fewer transitions and shorter durations for a lighter-weight mascot. Approximately 144 credits - half the cost of the full template. ## List Templates Fetch all available templates - both built-in and your own saved templates. Use the `source` query parameter to filter. GET /v1/canvas-templates ```bash # All templates (built-in + yours) curl https://api.masko.ai/v1/canvas-templates \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Only built-in templates curl "https://api.masko.ai/v1/canvas-templates?source=builtin" \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Only your saved templates curl "https://api.masko.ai/v1/canvas-templates?source=mine" \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```json { "data": [ { "id": "claude-code-4state", "name": "Claude Code 4-State", "description": "4 poses with 16 transitions for a full interactive mascot", "source": "builtin", "nodes": [ { "key": "idle", "name": "Idle", "imagePrompt": "standing relaxed, arms at sides" }, { "key": "thinking", "name": "Thinking", "imagePrompt": "hand on chin, looking up" }, { "key": "talking", "name": "Talking", "imagePrompt": "mouth open, gesturing" }, { "key": "celebrating", "name": "Celebrating", "imagePrompt": "arms raised, excited" } ], "edges": [ ... ], "inputs": [ ... ] }, { "id": "my-custom-template", "name": "My 3-State Widget", "description": "Custom template for sidebar widget", "source": "user", "public": false, "nodes": [ ... ], "edges": [ ... ], "created_at": "2026-03-25T14:30:00Z" } ] } ``` ## Apply a Template Apply a template to an existing canvas. This creates items for each node, generates images, and saves the graph. The response includes a `node_mapping` that maps template node keys to the created item and asset IDs, plus a list of generation jobs. POST /v1/collections/:id/canvases/:canvasId/from-template ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/from-template \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template_id": "claude-code-4state" }' ``` ```json { "data": { "graph": { "nodes": [ { "id": "ast_idle_001", "x": 0, "y": 0, "itemName": "Idle" }, { "id": "ast_think_002", "x": 300, "y": 0, "itemName": "Thinking" }, { "id": "ast_talk_003", "x": 0, "y": 300, "itemName": "Talking" }, { "id": "ast_celeb_004", "x": 300, "y": 300, "itemName": "Celebrating" } ], "edges": [ ... ], "viewport": { "x": 0, "y": 0, "zoom": 0.8 }, "inputs": [ ... ] }, "jobs": [ { "job_id": "job_t001", "node_key": "idle" }, { "job_id": "job_t002", "node_key": "thinking" }, { "job_id": "job_t003", "node_key": "talking" }, { "job_id": "job_t004", "node_key": "celebrating" } ], "node_mapping": { "idle": { "item_id": "item_idle_01", "asset_id": "ast_idle_001", "urls": { ... } }, "thinking": { "item_id": "item_think_02", "asset_id": "ast_think_002", "urls": { ... } }, "talking": { "item_id": "item_talk_03", "asset_id": "ast_talk_003", "urls": { ... } }, "celebrating": { "item_id": "item_celeb_04", "asset_id": "ast_celeb_004", "urls": { ... } } }, "total_cost": 4 } } ``` The `total_cost` from applying a template only covers image generation (1 credit per node). Animation generation is a separate step via `generate-all` which costs 5 credits/sec per transition. ## Customize with Overrides Pass `node_overrides` and `edge_overrides` to customize template prompts and names without modifying the template itself. Node overrides are keyed by the template's node key. Edge overrides are keyed by `source->target` format. ```bash curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/from-template \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template_id": "claude-code-4state", "node_overrides": { "idle": { "name": "Resting", "imagePrompt": "sitting peacefully on a rock, eyes half-closed" }, "celebrating": { "imagePrompt": "doing a backflip with sparkles" } }, "edge_overrides": { "idle->thinking": { "description": "slowly standing up and scratching head" } } }' ``` ## Save Your Own Save a template from an existing canvas or from a raw JSON graph definition. Set `public: true` to make it available to other users. POST /v1/canvas-templates ```bash 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 Widget Template", "description": "3-state sidebar widget with hover and click", "canvas_id": "CANVAS_ID", "collection_id": "COLLECTION_ID", "public": false }' ``` ```bash curl -X POST https://api.masko.ai/v1/canvas-templates \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Simple 2-State", "description": "Minimal idle/active toggle", "template": { "nodes": [ { "key": "idle", "name": "Idle", "imagePrompt": "standing relaxed", "x": 0, "y": 0 }, { "key": "active", "name": "Active", "imagePrompt": "alert and ready", "x": 300, "y": 0 } ], "edges": [ { "source": "idle", "target": "active", "duration": 3, "description": "becoming alert", "conditions": [{ "input": "isActive", "op": "eq", "value": true }] }, { "source": "active", "target": "idle", "duration": 3, "description": "relaxing back", "conditions": [{ "input": "isActive", "op": "eq", "value": false }], "reverse": true, "reverseOfEdge": "idle->active" } ], "inputs": [ { "name": "isActive", "type": "boolean", "defaultValue": false } ] }, "public": true }' ``` ```json { "data": { "id": "tmpl_abc123", "name": "Simple 2-State", "source": "user", "public": true, "created_at": "2026-03-28T12:00:00Z" } } ``` --- ---url: /docs/canvas/generate-all--- # Generate All Edge Videos Generate transition videos for every edge in your canvas that does not yet have a video. One call kicks off all the jobs and returns a summary of what was queued. POST /v1/collections/:id/canvases/:canvasId/generate-all ```bash 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 '{ "skip_completed": true, "duration": 4 }' ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/collections/${collectionId}/canvases/${canvasId}/generate-all`, { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ skip_completed: true, duration: 4, }), } ); const { jobs, total_credits } = await res.json(); console.log(`Queued ${jobs.length} jobs for ${total_credits} credits`); ``` ## How It Works The endpoint scans every edge in the canvas and generates a video for each one that is missing a `videoAssetId`. The `skip_completed` parameter defaults to `true`, meaning edges that already have a completed video are skipped. Set it to `false` to regenerate everything from scratch. Each generated job follows the same lifecycle as a regular animation job - you can poll individual jobs or use the canvas status endpoint to track overall progress. ## Edge Classification Not all edges cost the same. The endpoint classifies each edge before generation: | Type | Condition | Cost | Notes | | --- | --- | --- | --- | | Loop | source == target | Paid | Looping animation on a single pose | | Forward | source != target | Paid | Transition from one pose to another | | Reverse | Auto-generated from forward | 0 credits | Triggered automatically when forward completes | | Any State | source = "*" | Skipped | Virtual edges resolved at runtime, no video needed | ## Cost Calculation Only loops and forward transitions cost credits. The formula is: `(loops + forwards) x 5 x duration_seconds` For example, a canvas with 4 loop edges and 3 forward edges at 4 seconds each: ```text Paid edges: 4 loops + 3 forwards = 7 Cost per edge: 5 credits/sec x 4 sec = 20 credits Total: 7 x 20 = 140 credits Reverse edges (auto): 3 (one per forward) = 0 credits Any State edges: skipped = 0 credits ``` The response includes a `total_credits` field so you know the exact cost before generation begins. If you do not have enough credits, the request returns a 402 error with the required amount. ## Auto-Reverse When a forward transition completes (e.g. Idle to Waving), the reverse transition (Waving to Idle) is automatically triggered at 0 credits. You do not need to request it separately. The reverse job appears in the canvas status response alongside the forward jobs. If a forward edge already has a matching reverse edge in the canvas, the auto-reverse fills in the reverse edge video. If no reverse edge exists, one is created automatically. ## Poll Progress After kicking off generation, poll the canvas status endpoint to track overall progress: GET /v1/collections/:id/canvases/:canvasId/status ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/status \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "canvas_id": "abc-123", # "total_edges": 14, # "completed": 10, # "pending": 3, # "failed": 1, # "ready": false, # "progress": 0.71 # } ``` ```javascript async function waitForCanvas(collectionId, canvasId) { while (true) { const res = await fetch( `https://api.masko.ai/v1/collections/${collectionId}/canvases/${canvasId}/status`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const status = await res.json(); console.log(`Progress: ${Math.round(status.progress * 100)}%`); if (status.ready) { console.log('All edges have videos. Canvas is ready to export.'); return status; } if (status.failed > 0) { console.warn(`${status.failed} edges failed`); } await new Promise((r) => setTimeout(r, 5000)); } } ``` When `ready` is `true`, every edge in the canvas has a completed video and the canvas can be exported. You can safely call the export endpoint at that point. --- ---url: /docs/canvas/export--- # Export Canvas Once all edge videos are generated, export the canvas as a `MaskoAnimationConfig` JSON object. This config contains everything needed to run your interactive mascot - nodes, edges, video URLs, and input mappings. GET /v1/collections/:id/canvases/:canvasId/export ```bash curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/export \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/collections/${collectionId}/canvases/${canvasId}/export`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const config = await res.json(); // Pass config to your Masko embed player ``` ## The MaskoAnimationConfig The `MaskoAnimationConfig` is a versioned JSON format (current version `2.0`) that describes an interactive mascot state machine. It is the output of the export endpoint and the input to the Masko embed player. The config is self-contained - all asset URLs point to the CDN and can be used directly in a browser or desktop app. ## Response Structure ```json { "version": "2.0", "name": "My Mascot", "initialNode": "idle", "autoPlay": true, "clickEffect": "next_state", "nodes": [ { "id": "idle", "label": "Idle", "imageUrl": "https://assets.masko.ai/col_abc/idle.png", "loop": true }, { "id": "waving", "label": "Waving", "imageUrl": "https://assets.masko.ai/col_abc/waving.png", "loop": false }, { "id": "thinking", "label": "Thinking", "imageUrl": "https://assets.masko.ai/col_abc/thinking.png", "loop": true } ], "edges": [ { "id": "idle-loop", "source": "idle", "target": "idle", "videos": { "webm": "https://assets.masko.ai/col_abc/idle-loop.webm", "hevc": "https://assets.masko.ai/col_abc/idle-loop.mp4" }, "condition": "default" }, { "id": "idle-to-waving", "source": "idle", "target": "waving", "videos": { "webm": "https://assets.masko.ai/col_abc/idle-to-waving.webm", "hevc": "https://assets.masko.ai/col_abc/idle-to-waving.mp4" }, "condition": "on_click" }, { "id": "waving-to-idle", "source": "waving", "target": "idle", "videos": { "webm": "https://assets.masko.ai/col_abc/waving-to-idle.webm", "hevc": "https://assets.masko.ai/col_abc/waving-to-idle.mp4" }, "condition": "on_complete" } ], "inputs": [ { "id": "show_thinking", "label": "Show Thinking", "targetNode": "thinking", "returnNode": "idle" } ] } ``` ## Config Fields | Field | Type | Description | | --- | --- | --- | | `version` | string | Config format version. Currently "2.0". | | `name` | string | Display name of the canvas. | | `initialNode` | string | ID of the node to display on load. | | `autoPlay` | boolean | Start playing the initial loop automatically. | | `clickEffect` | string | What happens when the user clicks the mascot. e.g. "next_state". | | `nodes[]` | array | Mascot poses. Each has id, label, imageUrl, and loop flag. | | `edges[]` | array | Transitions between nodes. Each has source, target, videos (webm/hevc), and condition. | | `inputs[]` | array | Programmatic triggers. Each has id, label, targetNode, and returnNode. | ## Using in Web The config includes both `webm` and `hevc` video URLs for each edge. Use WebM for Chrome and Firefox, and HEVC (MP4) for Safari. Here is a minimal example for format selection: ```html
``` ## Using in Desktop The Masko Code desktop app loads the `MaskoAnimationConfig` directly. Export the config, save it as a JSON file, and point the app to it. The desktop player handles format selection, caching, and state machine logic automatically. The export endpoint only succeeds when all edges have completed videos. Check the `/status` endpoint first and wait for `ready: true` before exporting. --- ---url: /docs/manage/jobs--- # Jobs & Polling Every generation creates a job. Track progress and get results by polling the job endpoint, or use long-polling to wait for completion without repeated requests. ## Job Lifecycle Every job moves through a simple lifecycle: `pending` - `processing` - `completed` or `failed`. A job enters `pending` when the generation request is accepted, transitions to `processing` once a worker picks it up, and resolves to either `completed` with asset URLs or `failed` with an error message. ```text pending --> processing --> completed \--> failed ``` ## List Jobs Retrieve all your jobs with optional filters. Use query parameters to narrow results by status, collection, or type. GET /v1/jobs ```bash # List all jobs curl https://api.masko.ai/v1/jobs \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Filter by status and collection curl "https://api.masko.ai/v1/jobs?status=completed&collection_id=COLLECTION_ID" \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Filter by type curl "https://api.masko.ai/v1/jobs?type=animation" \ -H "Authorization: Bearer masko_YOUR_API_KEY" ``` ```javascript const params = new URLSearchParams({ status: 'completed', collection_id: collectionId, }); const res = await fetch( `https://api.masko.ai/v1/jobs?${params}`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const { jobs } = await res.json(); // jobs: [{ id, status, type, created_at, ... }, ...] ``` Available filters: `?status=pending|processing|completed|failed`, `?collection_id=...`, `?type=image|animation|logo|edit`. ## Get Job Detail Fetch a single job by ID. Completed jobs include full output data with asset IDs and CDN URLs. GET /v1/jobs/:id ```bash curl https://api.masko.ai/v1/jobs/JOB_ID \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Completed job response: # { # "id": "job_abc123", # "status": "completed", # "type": "animation", # "collection_id": "col_xyz", # "created_at": "2026-03-28T10:00:00Z", # "completed_at": "2026-03-28T10:01:32Z", # "output_data": { # "item_id": "item_456", # "duration": 4 # }, # "asset_ids": { # "image": "ast_img_001", # "transparent_image": "ast_timg_001", # "video": "ast_vid_001", # "webm": "ast_webm_001", # "hevc": "ast_hevc_001" # }, # "urls": { # "image": "https://assets.masko.ai/col_xyz/image.png", # "transparent_image": "https://assets.masko.ai/col_xyz/transparent.png", # "video": "https://assets.masko.ai/col_xyz/video.mp4", # "webm": "https://assets.masko.ai/col_xyz/video.webm", # "hevc": "https://assets.masko.ai/col_xyz/video_hevc.mp4" # } # } ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/jobs/${jobId}`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const job = await res.json(); if (job.status === 'completed') { console.log('Image URL:', job.urls.image); console.log('Video URL:', job.urls.webm); } else if (job.status === 'failed') { console.error('Job failed:', job.error); } ``` ## Long-Polling Instead of polling repeatedly, use long-polling. Add `?wait=true` and the server holds the connection open until the job completes or the timeout is reached. The default timeout is 120 seconds, configurable with `?timeout=`. GET /v1/jobs/:id?wait=true&timeout=120 ```bash # Wait up to 120 seconds for the job to complete curl "https://api.masko.ai/v1/jobs/JOB_ID?wait=true&timeout=120" \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Returns immediately if already completed. # Returns the job object when it completes. # Returns with current status if timeout is reached. ``` ```javascript async function waitForJob(jobId) { const res = await fetch( `https://api.masko.ai/v1/jobs/${jobId}?wait=true&timeout=120`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const job = await res.json(); if (job.status === 'completed') { return job; } if (job.status === 'failed') { throw new Error(job.error); } // Timed out but still processing - try again return waitForJob(jobId); } const job = await waitForJob('job_abc123'); console.log('Done:', job.urls); ``` ## When to Poll vs Webhooks - **Long-polling** - simplest approach. Best for scripts, CLI tools, and prototyping. One request, one response, no infrastructure needed. - **Webhooks** - best for production. Your server gets notified when jobs complete. No open connections, handles high volumes, works behind load balancers. - **Hybrid** - use webhooks for background processing and long-polling for user-facing flows where you need immediate feedback. - [Webhooks](/docs/manage/webhooks) - Get notified when jobs complete instead of polling. Set up webhook endpoints for production use. - [List & Browse](/docs/manage/collections) - Browse your projects, collections, and assets to find the resources you need. --- ---url: /docs/manage/webhooks--- # Webhooks Get notified when jobs complete instead of polling. Register a webhook URL and Masko sends a POST request with job results as soon as they are ready. ## Create a Webhook Register a webhook endpoint with the events you want to receive. The response includes a `secret` for verifying payloads. POST /v1/webhooks ```bash curl -X POST https://api.masko.ai/v1/webhooks \ -H "Authorization: Bearer masko_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/api/masko-webhook", "events": ["job.completed", "job.failed"] }' # Response: # { # "id": "wh_abc123", # "url": "https://your-app.com/api/masko-webhook", # "events": ["job.completed", "job.failed"], # "secret": "whsec_k7x9m2p4q8r1...", # "created_at": "2026-03-28T10:00:00Z" # } ``` ```javascript const res = await fetch('https://api.masko.ai/v1/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://your-app.com/api/masko-webhook', events: ['job.completed', 'job.failed'], }), }); const webhook = await res.json(); // Store webhook.secret securely - it is only shown once ``` Save the `secret` immediately. It cannot be retrieved later. If you lose it, delete the webhook and create a new one. ## Webhook Payload When a subscribed event fires, Masko sends a POST request to your URL with a JSON body: ```json { "event": "job.completed", "job_id": "job_abc123", "type": "animation", "collection_id": "col_xyz", "asset_ids": { "image": "ast_img_001", "transparent_image": "ast_timg_001", "video": "ast_vid_001", "webm": "ast_webm_001", "hevc": "ast_hevc_001" }, "urls": { "image": "https://assets.masko.ai/col_xyz/image.png", "transparent_image": "https://assets.masko.ai/col_xyz/transparent.png", "video": "https://assets.masko.ai/col_xyz/video.mp4", "webm": "https://assets.masko.ai/col_xyz/video.webm", "hevc": "https://assets.masko.ai/col_xyz/video_hevc.mp4" }, "timestamp": "2026-03-28T10:01:32Z" } ``` ```json { "event": "job.failed", "job_id": "job_abc123", "type": "animation", "collection_id": "col_xyz", "error": { "code": "generation_failed", "message": "Video generation timed out after 5 minutes" }, "timestamp": "2026-03-28T10:05:00Z" } ``` ## Verify Signatures Every webhook request includes a `X-Masko-Signature` header containing an HMAC-SHA256 signature of the request body. Verify it using the secret from when you created the webhook. ```javascript import crypto from 'crypto'; function verifyWebhook(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(body, 'utf-8') .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } // In your webhook handler: app.post('/api/masko-webhook', (req, res) => { const signature = req.headers['x-masko-signature']; const isValid = verifyWebhook( JSON.stringify(req.body), signature, process.env.MASKO_WEBHOOK_SECRET ); if (!isValid) { return res.status(401).send('Invalid signature'); } const { event, job_id, urls } = req.body; console.log(`Job ${job_id} ${event}:`, urls); res.status(200).send('OK'); }); ``` ```python import hmac import hashlib def verify_webhook(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode('utf-8'), body, hashlib.sha256, ).hexdigest() return hmac.compare_digest(signature, expected) # In your webhook handler (Flask example): @app.route('/api/masko-webhook', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-Masko-Signature', '') is_valid = verify_webhook( request.get_data(), signature, os.environ['MASKO_WEBHOOK_SECRET'], ) if not is_valid: return 'Invalid signature', 401 data = request.get_json() print(f"Job {data['job_id']} {data['event']}") return 'OK', 200 ``` ## Retry Policy If your endpoint returns a non-2xx status code or the request times out, Masko retries with exponential backoff: - **Retry 1:** after 1 second - **Retry 2:** after 5 seconds - **Retry 3:** after 30 seconds After 3 failed attempts, the delivery is marked as failed. If a webhook accumulates 100 consecutive delivery failures, it is automatically disabled. You can re-enable it by deleting and recreating it. ## Manage Webhooks List all your registered webhooks: GET /v1/webhooks ```bash curl https://api.masko.ai/v1/webhooks \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "webhooks": [ # { # "id": "wh_abc123", # "url": "https://your-app.com/api/masko-webhook", # "events": ["job.completed", "job.failed"], # "created_at": "2026-03-28T10:00:00Z" # } # ] # } ``` Delete a webhook when you no longer need it: DELETE /v1/webhooks/:id ```bash curl -X DELETE https://api.masko.ai/v1/webhooks/wh_abc123 \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: 204 No Content ``` --- ---url: /docs/manage/collections--- # List & Browse Browse your projects, collections, and assets. Use these endpoints to find resource IDs, check generation status, and retrieve CDN URLs. ## List Projects Projects are the top-level container. Each project can hold multiple collections. GET /v1/projects ```bash curl https://api.masko.ai/v1/projects \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "projects": [ # { # "id": "proj_abc123", # "name": "My SaaS", # "created_at": "2026-01-15T08:00:00Z" # }, # { # "id": "proj_def456", # "name": "Marketing Site", # "created_at": "2026-02-20T14:30:00Z" # } # ] # } ``` ```javascript const res = await fetch('https://api.masko.ai/v1/projects', { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, }); const { projects } = await res.json(); console.log(projects.map(p => p.name)); ``` ## List Collections List all collections, optionally filtered by project. Each collection represents one mascot character. GET /v1/collections ```bash # All collections curl https://api.masko.ai/v1/collections \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Filter by project curl "https://api.masko.ai/v1/collections?project_id=proj_abc123" \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "collections": [ # { # "id": "col_xyz789", # "project_id": "proj_abc123", # "name": "Fox Mascot", # "description": "A friendly fox character for our SaaS", # "created_at": "2026-02-01T10:00:00Z", # "item_count": 12, # "asset_count": 48 # } # ] # } ``` ```javascript const params = new URLSearchParams({ project_id: 'proj_abc123', }); const res = await fetch( `https://api.masko.ai/v1/collections?${params}`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const { collections } = await res.json(); ``` ## Collection Detail Get full details of a single collection, including its configuration with style card and reference settings. GET /v1/collections/:id ```bash curl https://api.masko.ai/v1/collections/col_xyz789 \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "id": "col_xyz789", # "project_id": "proj_abc123", # "name": "Fox Mascot", # "description": "A friendly fox character for our SaaS", # "config": { # "style_card": "Flat illustration style with bold outlines...", # "reference_asset_ids": ["ast_ref_001", "ast_ref_002"], # "caution_list": ["Avoid overly rounded ears", "Keep tail fluffy"] # }, # "created_at": "2026-02-01T10:00:00Z", # "updated_at": "2026-03-15T16:20:00Z" # } ``` ## List Items Items are individual poses or assets within a collection. Each item can have multiple asset types (image, animation, logo). GET /v1/collections/:id/items ```bash curl https://api.masko.ai/v1/collections/col_xyz789/items \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "items": [ # { # "id": "item_001", # "name": "Idle", # "type": "image", # "created_at": "2026-02-01T10:05:00Z", # "assets": { # "image": { "id": "ast_img_001", "status": "completed" }, # "transparent_image": { "id": "ast_timg_001", "status": "completed" } # } # }, # { # "id": "item_002", # "name": "Dancing", # "type": "animation", # "created_at": "2026-02-01T10:10:00Z", # "assets": { # "image": { "id": "ast_img_002", "status": "completed" }, # "transparent_image": { "id": "ast_timg_002", "status": "completed" }, # "video": { "id": "ast_vid_002", "status": "completed" }, # "webm": { "id": "ast_webm_002", "status": "completed" }, # "hevc": { "id": "ast_hevc_002", "status": "completed" } # } # } # ] # } ``` ```javascript const res = await fetch( `https://api.masko.ai/v1/collections/${collectionId}/items`, { headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY' }, } ); const { items } = await res.json(); const images = items.filter(i => i.type === 'image'); const animations = items.filter(i => i.type === 'animation'); ``` ## Check Asset Status List all assets in a collection with their current status. Useful for checking which generations are still in progress. GET /v1/collections/:id/assets ```bash curl https://api.masko.ai/v1/collections/col_xyz789/assets \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "assets": [ # { # "id": "ast_img_001", # "type": "image", # "status": "completed", # "url": "https://assets.masko.ai/col_xyz/image_001.png", # "item_id": "item_001" # }, # { # "id": "ast_vid_003", # "type": "video", # "status": "processing", # "url": null, # "item_id": "item_003" # }, # { # "id": "ast_webm_004", # "type": "webm", # "status": "pending", # "url": null, # "item_id": "item_004" # } # ] # } ``` ## Get All URLs For published collections, you can retrieve all CDN URLs in one call. This returns only completed assets that have been published to the CDN. GET /v1/collections/:id/urls ```bash curl https://api.masko.ai/v1/collections/col_xyz789/urls \ -H "Authorization: Bearer masko_YOUR_API_KEY" # Response: # { # "urls": { # "item_001": { # "image": "https://assets.masko.ai/col_xyz/idle.png", # "transparent_image": "https://assets.masko.ai/col_xyz/idle_transparent.png" # }, # "item_002": { # "image": "https://assets.masko.ai/col_xyz/dancing.png", # "webm": "https://assets.masko.ai/col_xyz/dancing.webm", # "hevc": "https://assets.masko.ai/col_xyz/dancing.mp4" # } # } # } ``` --- ---url: /docs/reference/errors--- # Error Codes %% animation https://assets.masko.ai/7fced6/spark-4735/confused-scratching-head-b90fd4d3-360.webm https://assets.masko.ai/7fced6/spark-4735/confused-scratching-head-4c52ba61-360.mov %% All API errors follow the same format. Here is how to handle each one. ## Error Response Format Every error response contains an `error` object with a machine-readable `code` and a human-readable `message`: ```json { "error": { "code": "insufficient_credits", "message": "Not enough credits. Required: 20, balance: 5." } } ``` ## Error Reference | HTTP | Error Code | Meaning | What To Do | |------|-----------|---------|------------| | 400 | `bad_request` | Invalid request body or missing required fields. | Check the `message` for which field failed validation. Fix the request and retry. | | 401 | `unauthorized` | Missing or invalid API key. | Verify your `Authorization: Bearer masko_...` header is correct and the key has not been revoked. | | 402 | `insufficient_credits` | Not enough credits for this operation. Response includes `required` and `balance`. | Top up credits at [app.masko.ai/billing](https://app.masko.ai/billing) or reduce the request scope. | | 403 | `forbidden` | You do not have access to this resource. | Verify the resource belongs to your account. Check that your API key has the necessary permissions. | | 404 | `not_found` | The requested resource does not exist. | Check the resource ID in the URL. Use the list endpoints to find valid IDs. | | 429 | `rate_limited` | Too many requests. Response includes `Retry-After` header. | Wait for the number of seconds in the `Retry-After` header, then retry. | | 500 | `internal_error` | Something went wrong on our side. | Retry after a short delay. If persistent, contact support at [paul@masko.ai](mailto:paul@masko.ai). | ## Handling Insufficient Credits The 402 response includes `required` and `balance` fields so you can show users exactly how many credits they need: ```javascript // HTTP 402 { "error": { "code": "insufficient_credits", "message": "Not enough credits. Required: 140, balance: 50.", "required": 140, "balance": 50 } } ``` ```javascript const res = await fetch( 'https://api.masko.ai/v1/collections/COLLECTION_ID/generate', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'animation', name: 'Dancing', image_prompt: 'dancing pose', animation_prompt: 'dancing', duration: 4, }), } ); if (res.status === 402) { const { error } = await res.json(); const needed = error.required - error.balance; console.log(`Need ${needed} more credits. Top up at app.masko.ai/billing`); return; } ``` ## Rate Limit Recovery When you hit a 429, the response includes a `Retry-After` header with the number of seconds to wait. Use exponential backoff for robustness: ```javascript async function fetchWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const res = await fetch(url, options); if (res.status !== 429) { return res; } if (attempt === maxRetries) { throw new Error('Rate limited after max retries'); } // Use Retry-After header, or exponential backoff const retryAfter = res.headers.get('Retry-After'); const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : Math.pow(2, attempt) * 1000; console.log(`Rate limited. Retrying in ${delay / 1000}s...`); await new Promise((r) => setTimeout(r, delay)); } } // Usage: const res = await fetchWithRetry( 'https://api.masko.ai/v1/collections/COLLECTION_ID/generate', { method: 'POST', headers: { 'Authorization': 'Bearer masko_YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'image', name: 'Hello', image_prompt: 'waving hello', }), } ); ``` Parse the `error.code` field programmatically to decide how to handle each error. Use `error.message` for logging and debugging - it may change between versions, but the code will stay stable. ---