# 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? Install the [Masko AI skill](/docs/ai-tools/skills) to give it the API contract and generation workflow.
Prerequisites: You need an API key. Create one in the [Developer dashboard](/api-keys).
## 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 { data: project } = await res.json();
```
## Step 2: Create a Collection
A collection represents a single mascot character. Give it a name and a prompt describing the character you want. If you create from text without reference images, Masko generates the first reference image for 1 credit.
```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",
"prompt": "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',
prompt: 'A friendly orange fox mascot wearing a blue scarf',
}),
});
const { data: collection } = await res.json();
// Save collection.id for the next steps
```
## Step 3: Generate an Animation
Request a generation by specifying the type, item name, and prompts. 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",
"name": "Waving hello",
"image_prompt": "waving hello",
"animation_prompt": "waving hello with a friendly hand wave",
"duration": 4
}'
```
```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: 'Waving hello',
image_prompt: 'waving hello',
animation_prompt: 'waving hello with a friendly hand wave',
duration: 4,
}),
}
);
const { data } = await res.json();
const job_id = data.job_id;
```
## 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:
# {
# "data": {
# "id": "JOB_ID",
# "status": "completed",
# "type": "item_generation",
# "urls": {
# "video": "https://...",
# "webm": "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 { data: job } = await res.json();
if (job.status === 'completed') {
return job.urls;
}
if (job.status === 'failed') {
throw new Error(job.error);
}
// Wait 2 seconds before polling again
await new Promise((r) => setTimeout(r, 2000));
}
}
const urls = await pollJob(job_id);
console.log(urls);
```
Once your assets are published to the CDN, the URLs are permanent and served from `assets.masko.ai` with global edge caching.
## 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) - Give a coding agent direct API guidance.
---
---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 { data: 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).
### 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 v1 API may return `429 Too Many Requests` when traffic is limited. Treat a `429` as retryable and back off before trying again.
## 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
AI coding agents can create mascots and generate assets through Masko's public REST API. This works in any agent that can make HTTP requests or run a script.
## Choose Your Integration
| Method | Best For | Setup |
|--------|----------|-------|
| [**AI Skill**](/docs/ai-tools/skills) | Give an agent Masko-specific workflows and API guidance | 2 minutes |
| **Direct API** | Scripts, applications, and custom agent tools | [Quickstart](/docs/quickstart) |
| **OpenAPI** | Generate a typed client or inspect the complete contract | [OpenAPI spec](https://api.masko.ai/v1/openapi.json) |
## Quick Setup
Create an API key in [Developer Settings](https://masko.ai/settings/developer), then pass it as a bearer token:
```bash
curl https://api.masko.ai/v1/credits \
-H "Authorization: Bearer $MASKO_API_KEY"
```
## What Agents Can Do
- Create and manage mascot collections
- Generate images, animations, edits, logos, and scenes
- Poll generation jobs and retrieve CDN URLs
- Build and update interactive canvases
- Manage projects, assets, and references
## Agent Resources
| Resource | URL |
|----------|-----|
| LLM-friendly docs | [masko.ai/llms-full.txt](https://masko.ai/llms-full.txt) |
| Documentation search | `GET https://api.masko.ai/v1/docs?q=animation` |
| OpenAPI specification | [api.masko.ai/v1/openapi.json](https://api.masko.ai/v1/openapi.json) |
In the API, a mascot is represented by a collection with `type: "mascot"`.
---
---url: /docs/ai-tools/skills---
# AI Skills
AI skills are instruction files that teach a coding agent how to call the Masko REST API and follow its generation workflows.
## Install for Your Tool
Copy `packages/skill/masko/SKILL.md` into the instruction location supported by your agent:
| Tool | Location |
|------|----------|
| Claude Code | `.claude/skills/masko/SKILL.md` |
| Cursor | `.cursor/rules/masko.mdc` |
| VS Code / Copilot | `.github/copilot-instructions.md` |
| Codex | `AGENTS.md` or a local skill directory |
| Gemini CLI | `GEMINI.md` |
| Windsurf | `.windsurfrules` |
## API Setup
Create an API key in [Developer Settings](https://masko.ai/settings/developer). The skill expects it in `MASKO_API_KEY` and calls the public API directly:
```bash
export MASKO_API_KEY=masko_YOUR_KEY
curl https://api.masko.ai/v1/credits \
-H "Authorization: Bearer $MASKO_API_KEY"
```
The complete contract is available in the [OpenAPI specification](https://api.masko.ai/v1/openapi.json), and the consolidated agent documentation is available at [masko.ai/llms-full.txt](https://masko.ai/llms-full.txt).
---
---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"` and managed through `/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 `settings.animation_sizes` when creating a collection, or update `publish_params.animation_sizes` later, to define numeric pixel sizes such as `[720, 480, 360, 240]`. 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:
- `POST /v1/analyze` - AI analysis of an uploaded image (`type: "image"`) or website (`type: "url"`)
- `GET /v1/collections/:id/suggestions` - 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: credits } = await res.json();
console.log(credits.total);
```
```json
{
"data": {
"subscription": 450,
"topup": 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 { data: styles } = await res.json();
```
```json
{
"data": [
{ "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 { data } = await res.json();
const images = data.images;
```
```json
{
"data": {
"images": [
{ "url": "https://...", "expires_in": 3600 },
{ "url": "https://...", "expires_in": 3600 }
],
"cost": 2
}
}
```
## 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 { data: collection } = await res.json();
console.log(collection.id); // Use this for generation
```
```json
{
"data": {
"id": "col_abc123",
"name": "Astro Fox",
"slug": "astro-fox-a1b2c3d4",
"type": "mascot",
"reference_asset_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"settings": {
"cdn_enabled": true,
"animation_sizes": []
}
}
}
```
CDN publishing is enabled by default on new collections. You can also set numeric `animation_sizes` at creation time to define which resolutions to generate, for example `[720, 480, 360]`.
## 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 { data } = await res.json();
const asset_id = data.asset_id;
```
### 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 { data } = await res.json();
const asset_id = data.asset_id;
```
```json
{
"data": {
"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 { data: collection } = await res.json();
// collection.name is auto-detected from the image
// collection.reference_asset_ids contains the linked reference
```
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
Send a website URL with `type: "url"` and the API will screenshot the page, extract page metadata, summarize the brand, and return any generated suggestions without base64 payloads. This endpoint is free.
```bash
curl -X POST https://api.masko.ai/v1/analyze \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "url", "url": "https://example.com" }'
```
```javascript
const res = await fetch('https://api.masko.ai/v1/analyze', {
method: 'POST',
headers: {
'Authorization': 'Bearer masko_YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ type: 'url', url: 'https://example.com' })
});
const { data } = await res.json();
const { markdown, metadata, description, suggestions } = data;
```
```json
{
"data": {
"description": "Developer tools for example teams",
"metadata": { "title": "Example Corp" },
"markdown": "# Example Corp\n\nDeveloper tools...",
"suggestions": [
{
"name": "Codey",
"prompt": "A friendly blue robot with rounded features..."
}
]
}
}
```
The same endpoint analyzes an image. Pass `type: "image"` and `image_url` to extract a character description and name from an existing mascot image.
## Use Suggestions
Take a suggestion from the response and pass its prompt 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..."
}'
```
```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
})
});
```
## 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/workflow---
# End-to-end generation flow
This guide walks through the full path from zero to an animated mascot in four API calls: create a project and collection, add references, generate a pose image, then animate that pose. Each step feeds IDs into the next, so follow them in order.
## Step 1: Create a project and collection
Projects are top-level containers. Each project holds one or more collections, and each collection represents one mascot character. Create the project first, then seed the collection with `reference_image_urls` so style extraction can kick off on the first generation.
POST /v1/projects
```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 SaaS"
}'
```
```json
{
"data": {
"id": "proj_abc123",
"name": "My SaaS",
"created_at": "2026-04-18T08:00:00Z"
}
}
```
POST /v1/collections
```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": "proj_abc123",
"name": "Felix the Fox",
"context": "Friendly brand mascot for our SaaS",
"reference_image_urls": [
"https://example.com/fox-front.png",
"https://example.com/fox-side.png"
]
}'
```
```json
{
"data": {
"id": "col_xyz789",
"name": "Felix the Fox",
"slug": "felix-the-fox-a1b2c3d4",
"type": "mascot",
"reference_asset_ids": ["ast_ref_001", "ast_ref_002"],
"settings": {
"cdn_enabled": true,
"animation_sizes": []
}
}
}
```
## Step 2: Add additional references (optional)
If you want to add more reference images after creating the collection, use the references endpoint. Up to 6 references total. Adding or removing references clears the cached style card so the next generation re-extracts it.
POST /v1/collections/:id/references
```bash
curl -X POST https://api.masko.ai/v1/collections/col_xyz789/references \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/fox-back.png"
}'
```
```json
{
"data": {
"reference_asset_ids": [
"ast_ref_001",
"ast_ref_002",
"ast_ref_003"
]
}
}
```
See [Reference images and style consistency](/docs/manage/references) for more on how references work.
## Step 3: Generate a pose image
Now generate a static image of the mascot in the pose you want to animate. The response returns `asset_ids.image`, which is the ID you will need in step 4.
POST /v1/collections/:id/generate
```bash
curl -X POST https://api.masko.ai/v1/collections/col_xyz789/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_url": "/api/v1/jobs/job_abc123"
}
```
Poll `/v1/jobs/job_abc123?wait=true` until the job reaches `completed`. You can use the `asset_ids.image` returned by the generate response as `source_image_asset_id` in the next step.
## Step 4: Animate that pose
Call the same `/generate` endpoint again with `type: "animation"` and set `source_image_asset_id` to the `asset_ids.image` value captured in step 3. The animation is built from that exact pose.
POST /v1/collections/:id/generate
```bash
curl -X POST https://api.masko.ai/v1/collections/col_xyz789/generate \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"item_id": "item_def456",
"source_image_asset_id": "ast_img_001",
"animation_prompt": "waving arm back and forth in a friendly greeting",
"duration": 4
}'
```
```json
{
"data": {
"job_id": "job_anim456",
"status": "pending",
"type": "animation",
"item_id": "item_def456",
"estimated_cost": 15,
"asset_ids": {
"video": "ast_vid_003",
"webm": "ast_webm_003",
"hevc": "ast_hevc_003"
}
},
"poll_url": "/api/v1/jobs/job_anim456"
}
```
`source_image_asset_id` MUST be the `asset_ids.image` value returned by step 3's generate response. It must not be:
- A preview URL from `/v1/generate/preview` (previews are ephemeral and not persisted as assets)
- A `transparent_image` asset ID (source must be a full image, not the background-removed variant)
- A collection reference asset ID (references are style anchors, not poses to animate)
## Next steps
- [Animations guide](/docs/generate/animations) covers transitions between poses, auto-reverse looping, and size variants
- [Reference images and style consistency](/docs/manage/references) explains the three kinds of reference images and when to use each
---
---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_url": "/api/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_url": "/api/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/manage/references), 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_url": "/api/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_url": "/api/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_url": "/api/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 **4 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 new 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, new animation outputs automatically generate resized variants alongside the original.
To backfill missing size variants for existing completed animations, use the dedicated endpoint. Normal usage sends an empty body; it uses the collection's configured sizes, or `[360]` if no sizes are configured, and only creates missing variants.
POST /v1/collections/:id/size-variants
```bash
curl -X POST https://api.masko.ai/v1/collections/COL_ID/size-variants \
-H "Authorization: Bearer masko_..." \
-H "Content-Type: application/json" \
-d '{}'
```
Do not re-toggle `PATCH /v1/collections/:id/settings` to force a backfill. The `size-variants` endpoint is the idempotent repair path. `force: true` exists only for explicit recovery when you intentionally want to regenerate existing variants.
### 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": 4,
"sizes": [480]
}'
```
The response includes both original and variant URLs:
```json
{
"data": {
"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. The endpoint returns `202 Accepted` with the list of queued jobs - generation runs asynchronously.
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.
GET /v1/collections/:id/suggestions
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID/suggestions \
-H "Authorization: Bearer masko_YOUR_API_KEY"
```
```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/fda8417d/felix-the-fox/waving-hello-a1b2c3d4.png
```
- **user_prefix** - Your account prefix, set automatically.
- **collection_slug** - Derived from the collection name. Can be changed by patching the collection's `slug` field.
- **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",
"image_prompt": "standing and waving",
"animation_prompt": "waving hello",
"duration": 4,
"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
CDN publishing status for the collection is returned as the `cdn_status` field on `GET /v1/collections/:id`. It lists which assets have been published, their file sizes, and current status.
GET /v1/collections/:id
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY"
```
```json
{
"data": {
"id": "COLLECTION_ID",
"name": "Felix the Fox",
"slug": "felix-the-fox",
"cdn_status": [
{
"asset_id": "ast_img_001",
"item_name": "Waving hello",
"type": "image",
"cdn_url": "https://assets.masko.ai/fda8417d/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/fda8417d/felix-the-fox/idle-breathing-c3d4.webm",
"status": "completed",
"file_size": 1548200
}
]
}
}
```
## List Collection Assets
Retrieve assets for a collection. Returns `cdn_url` when available, falling back to signed `file_url`.
For metadata-only lists, add `?include_file_urls=false` to skip signed `file_url` generation.
GET /v1/collections/:id/assets
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID/assets \
-H "Authorization: Bearer masko_YOUR_API_KEY"
```
```json
{
"data": [
{
"id": "ast_img_001",
"type": "image",
"status": "completed",
"file_url": "https://storage.googleapis.com/...",
"cdn_url": "https://assets.masko.ai/fda8417d/felix-the-fox/waving-hello-a1b2.png",
"item_id": "item_123"
}
],
"meta": {
"pagination": { "total": 1, "limit": 50, "offset": 0, "has_more": false }
}
}
```
## Export Get Links JSON
Use `GET /v1/collections/:id/cdn-export` when you want the same copy-paste JSON shown in the collection page **Get Links** export modal. This response is not a raw asset list: it groups images, transparent images, animation videos, size variants, and logos by item name so it can be handed directly to an app.
This endpoint only returns published CDN assets. If asset hosting is disabled, the collection is not published, or no CDN assets exist yet, it returns `409 cdn_export_not_ready`.
GET /v1/collections/:id/cdn-export
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID/cdn-export \
-H "Authorization: Bearer masko_YOUR_API_KEY"
```
```json
{
"collection": "Felix the Fox",
"items": [
{
"name": "idle",
"image": "https://assets.masko.ai/fda8417d/felix-the-fox/idle-a1b2.png",
"transparent_image": "https://assets.masko.ai/fda8417d/felix-the-fox/idle-c3d4.png",
"animations": [
{
"video": "https://assets.masko.ai/fda8417d/felix-the-fox/idle-e5f6.mp4",
"transparent_video_webm": "https://assets.masko.ai/fda8417d/felix-the-fox/idle-g7h8.webm",
"transparent_video_android_360": "https://assets.masko.ai/fda8417d/felix-the-fox/idle-i9j0-360.mp4"
}
]
}
]
}
```
## Change Slug
Update the collection's CDN slug by patching the collection. 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
```bash
curl -X PATCH https://api.masko.ai/v1/collections/COLLECTION_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "felix"
}'
```
```json
{
"data": {
"id": "COLLECTION_ID",
"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 `settings.cdn_enabled: false` when creating the collection. 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 { data } = await res.json();
const { job_id, asset_ids, urls } = data;
```
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 { data } = await res.json();
const { job_id, asset_ids, urls } = data;
// 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 entry in the batch follows the same format as the single generate endpoint. Returns `202 Accepted` with the queued jobs.
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" },
{ "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 { data: job } = await res.json();
if (job.status === 'completed') {
console.log('URLs:', job.urls);
}
```
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 inputs so the runtime can resolve app or user activity into mascot behavior.
## 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 depend on resolved inputs such as `behavior::interact`, `behavior::attention`, or `node::nodeTime`. Raw hover and click events are normalized by the runtime before new desktop graphs see them.
%% /features %%
## Create a Canvas
Create a canvas within a collection. A canvas starts empty, or you can pass a `template_id` to populate it with a pre-built set of nodes and edges in the same call.
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 { data: canvas } = await res.json();
// canvas.id is used in subsequent requests
```
## Create from a Template
Templates define a pre-built set of nodes and edges. Pass `template_id` to the same `POST /canvases` endpoint to populate the new canvas with poses and transitions and kick off image generation for each node.
POST /v1/collections/:id/canvases
```bash
# Create a new canvas from the Claude Code 4-state template
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Canvas",
"template_id": "claude-code-4state"
}'
```
```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',
template_id: 'claude-code-4state'
})
}
);
const { data } = await res.json();
// data.graph.nodes, data.graph.edges, data.jobs, data.node_mapping
```
## Update a Graph Safely
Read the current canvas, modify its complete graph locally, and replace it with the revision hash returned by that read. Masko preserves generated fields for unchanged node and edge IDs. A stale revision returns `409`, so concurrent editors cannot silently overwrite each other.
GET /v1/collections/:id/canvases/:canvasId
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY"
```
Save the returned `data.graph` to `canvas.json`, edit that complete graph, and send it back with `data.graph_content_hash`:
PATCH /v1/collections/:id/canvases/:canvasId
```bash
curl -X PATCH https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg expected_graph_hash 'HASH_FROM_GET' \
--slurpfile graph canvas.json \
'{graph:$graph[0], expected_graph_hash:$expected_graph_hash}')"
```
For server-owned edge metadata changes such as playback speed, the dedicated atomic edge endpoint remains available:
PATCH /v1/collections/:id/canvases/:canvasId/edges/:edgeId
```bash
curl -X PATCH https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/edges/idle-to-interact \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "speed": 3 }'
```
## Generate Canvas Assets
Generate pending node images and/or transition animations in a single call. Use `dry_run: true` to preview the planned work, prompts, and credit cost before spending credits. The preview returns `plan_id` and `graph_content_hash`; send them back as `approved_plan_id` and `expected_graph_hash` when executing. Use `targets: "images"` for a pose review pass, then `targets: "animations"` after the poses look right. Pass `node_ids` and `edge_ids` to select an exact part of a larger graph. Reverse animations are free when the graph marks the return edge as a reverse of the forward edge.
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 '{
"dry_run": true,
"targets": "animations",
"edge_ids": ["idle-to-wave", "wave-loop", "wave-to-idle"],
"duration": 4,
"skip_completed": true
}'
# After approving the returned plan and price:
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 '{
"targets": "animations",
"edge_ids": ["idle-to-wave", "wave-loop", "wave-to-idle"],
"approved_plan_id": "PLAN_ID_FROM_DRY_RUN",
"expected_graph_hash": "GRAPH_HASH_FROM_DRY_RUN"
}'
```
The response includes `plan_id`, `graph_content_hash`, the normalized `selection`, `planned_jobs`, `generated`, `skipped_items`, `reverse_free`, `already_complete`, `estimated_cost`, `would_charge_credits`, and `actual_cost`. Unknown selected IDs fail before any work is queued or credits are spent.
Reverse transitions are free (0 credits). When generating a transition from A to B with a reverse edge that references it, the B to A animation is created automatically at no additional cost.
## Check Progress
Canvas generation progress is returned as the `status` field on the canvas detail response. Poll `GET /v1/collections/:id/canvases/:canvasId` and read `status` to see node and edge counts.
Use `status.media` as the main readiness interface:
- `status.media.generation.ready` means node images and parent edge videos are complete.
- `status.media.preview.ready` means the canvas editor can play every concrete edge using base WebM and HEVC derivatives.
- `status.media.preview.waiting_edges` means parent videos exist but the original generation job is still preparing derivatives; wait and poll again.
- `status.media.preview.repairable` means parent videos exist, no source job is still preparing derivatives, and missing base preview derivatives can be repaired safely.
- `status.media.variants` reports optimized size variants that exist or are incomplete; preview repair does not create size variants.
- `status.failed_nodes` and `status.failed_edges` contain terminal generation failures with a job ID and customer-safe error message. Review the message before asking for approval for a new paid attempt.
`status.nodes.pending` continues to count every incomplete node, including failed nodes, for backward compatibility. `status.nodes.failed` is the terminal subset.
`status.ready`, `status.generated_ready`, and `status.preview_ready` are kept for backward compatibility.
GET /v1/collections/:id/canvases/:canvasId
```bash
curl https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID \
-H "Authorization: Bearer masko_YOUR_API_KEY"
# Response:
# {
# "data": {
# "id": "...",
# "graph": { ... },
# "status": {
# "nodes": { "total": 4, "completed": 4, "pending": 0, "failed": 0 },
# "edges": { "total": 12, "completed": 8, "pending": 3, "failed": 1 },
# "ready": false,
# "generated_ready": false,
# "preview_ready": false,
# "media": {
# "generation": {
# "ready": false,
# "nodes": { "total": 4, "completed": 4, "pending": 0, "failed": 0 },
# "edges": { "total": 12, "completed": 8, "pending": 3, "failed": 1 }
# },
# "preview": {
# "ready": false,
# "repairable": false,
# "repairable_edges": 0,
# "waiting_edges": 0,
# "missing_edges": 4,
# "missing_formats": ["webm", "hevc"]
# },
# "variants": { "sizes": [], "missing": [] }
# },
# "failed_nodes": [],
# "failed_edges": []
# }
# }
# }
```
If `status.media.preview.waiting_edges` is greater than zero, wait and poll the canvas again. If `status.media.preview.repairable` is true, repair missing base derivatives with a dry-run first. Inspect `status.edge_media[].base.missing` only when you need the exact edge-level missing formats.
POST /v1/collections/:id/canvases/:canvasId/repair
```bash
curl -X POST https://api.masko.ai/v1/collections/COLLECTION_ID/canvases/CANVAS_ID/repair \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": "video_derivatives",
"dry_run": true
}'
```
Only call again with `"dry_run": false` after checking the returned `repairs`, `in_flight`, and `skipped` lists. This repair does not regenerate animations, does not run collection-wide generation, does not create size variants, and does not archive existing assets.
## Export
Export the canvas as a `MaskoAnimationConfig` JSON object ready to use with the Masko embed player. All asset URLs point to the CDN.
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 { data: config } = await res.json();
// Pass config to your Masko embed player
//