Animations
Animate your mascot on a transparent background: a loop that repeats, or a transition from one pose to another. Each animation comes as a transparent WebM (Chrome, Firefox, Edge), a transparent HEVC .mov (Safari, Apple devices) and an MP4.
Ko's idle loop at its 360-pixel size.
Upload and animate in one request
Upload the image with POST /v1/upload, then animate its data.asset_id. create_mascot creates the mascot in the same request:
curl -X POST https://api.masko.ai/v1/animations \
-H "Authorization: Bearer $MASKO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: first-wave-001" \
-d '{
"source_image_asset_id": "UPLOADED_IMAGE_ID",
"create_mascot": { "project_id": "PROJECT_ID" },
"name": "Wave",
"animation_prompt": "Wave gently, keeping the feet planted.",
"animation_model": "standard",
"duration": 5
}'- Defaults: Standard, 5 seconds, looping, 10 credits. Naming, mascot creation and cropping are free; non-square images get a centered square crop (image framing).
- Without
create_mascot.name, AI picks a short name such as Pip (Mascot <suffix>if naming fails) and writes no description. The upload becomes the mascot's first reference as is. - The
202response hasdata.mascot_id,mascot_name,mascot_created,job_id,item_id,asset_ids,estimated_costandpoll_url.
Each create_mascot makes a new mascot, so send the returned mascot_id for the next animation of that character:
{
"mascot_id": "RETURNED_MASCOT_ID",
"source_image_asset_id": "UPLOADED_IMAGE_ID",
"name": "Bounce",
"animation_prompt": "Bounce gently in place."
}Send exactly one of mascot_id (optionally with variant_id) or create_mascot. Retry an uncertain response with the same Idempotency-Key and body; if generation fails after the mascot was created, reuse error.details.mascot_id with a new key.
New pose and animation
Send an image_prompt for the pose and an animation_prompt for the motion. Five seconds cost 11 credits with Standard (1 for the image) and 31 with Premium.
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"animation_model": "standard",
"name": "Idle breathing",
"image_prompt": "standing relaxed with arms at sides",
"animation_prompt": "gentle breathing motion, subtle body sway",
"duration": 5,
"loop": true
}'Animate an existing image
Pass source_image_asset_id to skip image generation: 10 credits for 5 seconds with Standard.
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"animation_model": "standard",
"name": "Waving animated",
"source_image_asset_id": "3c8ce568-35ce-59c2-b3f8-9fb7e4a16e52",
"animation_prompt": "waving hello with smooth arm motion",
"duration": 5,
"loop": true
}'From Video
Copy a video's motion onto your mascot, Premium only: 6 credits per output second plus 1 image credit, 4 to 30 whole seconds, 31 credits at the default 5.
Upload the video (MP4 or WebM, up to 50 MB) and its first frame (PNG, JPEG or WebP, up to 10 MB), then send both IDs:
curl -X POST https://api.masko.ai/v1/upload \
-H "Authorization: Bearer $MASKO_API_KEY" \
-F "file=@motion.mp4"
curl -X POST https://api.masko.ai/v1/upload \
-H "Authorization: Bearer $MASKO_API_KEY" \
-F "file=@first-frame.png"
curl -X POST https://api.masko.ai/v1/mascots/$MASCOT_ID/animations/reference-video \
-H "Authorization: Bearer $MASKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_asset_id": "VIDEO_ASSET_ID",
"first_frame_asset_id": "FRAME_ASSET_ID",
"name": "Friendly wave from video",
"animation_model": "premium",
"duration": 5
}'- Both sources must be completed, active and accessible to your key. Optional:
variant_id,context_asset_id(reuse a mascot asset's saved context when no variant is set) andprompt(pose or look). - The
202response hasdata.job_id,poll_url,estimated_costandasset_ids, for one animation item and two pose items (item_id,start_item_id,end_item_id). - Poll
GET /v1/jobs/{job_id}?api_version=2026-09-26without resubmitting, then fetch each file withGET /v1/assets/{id}.
Transitions
Send both source_image_asset_id and end_image_asset_id; loop becomes false. 10 credits for 5 seconds with Standard.
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"animation_model": "standard",
"name": "Idle to Wave",
"source_image_asset_id": "6aec5b1a-cb7f-5f10-8d6e-235ec16b4d97",
"end_image_asset_id": "18d4fd5e-7193-57f1-8af9-f3bdff007783",
"animation_prompt": "smoothly transitioning from idle stance to waving",
"duration": 5
}'Variant context. A loop uses its source pose's saved context, even on a canvas that defaults to Original, and a transition keeps its start and end contexts separately.
- A conflicting
variant_idreturns409, unless that variant lists the image among its references. - An upload with no generation receipt uses the
variant_idthat references it, else Original if it does, else the only variant that does. - If several variants share an upload and Original does not, the request returns
409before charging. Pick a variant or generate distinct poses first.
Auto-reverse
On a transition, auto_reverse: true also makes the return clip (B to A), named by reverse_name, for 0 extra credits. It comes back as reverse_job.
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
-H "Authorization: Bearer masko_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"animation_model": "standard",
"name": "Idle to Wave",
"source_image_asset_id": "6aec5b1a-cb7f-5f10-8d6e-235ec16b4d97",
"end_image_asset_id": "18d4fd5e-7193-57f1-8af9-f3bdff007783",
"animation_prompt": "transitioning from idle to waving",
"auto_reverse": true,
"reverse_name": "Wave to Idle",
"duration": 5
}'Models, duration and looping
animation_model works on /generate, each generate-batch request, and canvas edge and generate-all requests.
animation_model | Duration | Default | Credits per second |
|---|---|---|---|
standard | 2–15 whole seconds | 5s | 2 |
premium | 4–30 whole seconds | 5s | 6 |
A duration outside the range returns 400 before charging. loop defaults to true (false for transitions) and asks for a seamless clip; your player decides whether to repeat it.
Output formats
Each animation has video (MP4, opaque), webm (VP9 with alpha), hevc (MOV with alpha) and stacked_video (MP4 with color and alpha stacked, for Android). See hosting and file formats.
Size variants
Every animation has its files at 720 px. Set up to two smaller animation_sizes, such as 480, 360, 240 or 128, in the mascot settings to get free resized copies of new animations. Sizes of 720 or more are ignored, and a third smaller size is refused.
curl -X PATCH https://api.masko.ai/v1/mascots/MASCOT_ID/settings \
-H "Authorization: Bearer masko_..." \
-H "Content-Type: application/json" \
-d '{
"publish_params": {
"animation_sizes": {
"enabled": true,
"sizes": [480, 240]
}
}
}'For finished animations, call the backfill with an empty body. It uses the configured sizes, or [360], and makes only the files each animation lacks: WebM, HEVC, stacked MP4 and stacked AV1, at its own width and at each size. Files that exist keep their links, and animations made before AV1 get their AV1 files.
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/size-variants \
-H "Authorization: Bearer masko_..." \
-H "Content-Type: application/json" \
-d '{}'Do not toggle the settings to force a backfill. force: true regenerates existing variants, for explicit recovery only.
Instant CDN URLs for size variants
Pass sizes when generating to get variant URLs at once:
curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
-H "Authorization: Bearer masko_..." \
-H "Content-Type: application/json" \
-d '{
"type": "animation",
"animation_model": "standard",
"name": "wave",
"image_prompt": "standing with a smile",
"animation_prompt": "waving hello",
"duration": 5,
"sizes": [480]
}'Only sizes enabled in the settings come back, and each URL serves a placeholder until its file is ready; poll the job's size_variants.status.
Direct uploads
source_image_asset_id also takes your own unattached POST /v1/upload image. It is imported as a source under the mascot, with the original file kept, no image generation and no change to mascot references. A new upload can take an approved variant_id; existing sources keep their context.
Image framing
Animations use square images. Other images, including those from item_id and transition end frames, are center-cropped into a separate 1024 × 1024 source; originals and references stay unchanged, and cropping is free.
For an off-center character, set source_image_crop:
{
"type": "animation",
"name": "Wave",
"source_image_asset_id": "IMAGE_ASSET_ID",
"source_image_crop": { "x": 200, "y": 0, "width": 800, "height": 800 },
"animation_prompt": "Wave gently.",
"animation_model": "standard",
"duration": 5
}- Coordinates are integer pixels after EXIF orientation; this takes 800 × 800 from an image at least 1000 × 800.
- The crop must be square (within one pixel) and inside the image, or the request returns
400before any job or charge. end_image_cropcrops the end frame and needsend_image_asset_id. Crops do not apply when reversing a video.
