Skip to content
maskostudioDocs

All pages

  • Overview/docs/use-cases
  • Mobile app/docs/use-cases#mobile
  • Website/docs/use-cases#website
  • Desktop/docs/use-cases#desktop
  • AI agents/docs/use-cases#agents
  • Videos/docs/use-cases#videos
  • Brand/docs/use-cases#brand
  • Start building/docs/ai-agents
  • Set up once/docs/ai-agents#set-up-once
  • Ask in plain words/docs/ai-agents#ask-in-plain-words
  • What your agent does/docs/ai-agents#what-your-agent-does
  • MCP server/docs/mcp
  • Masko for Cursor/docs/ai-tools/cursor
  • Masko in ChatGPT/docs/ai-tools/chatgpt
  • Start here/docs/tutorials#start-here
  • All tutorials/docs/tutorials
  • Create your first mascot/docs/tutorials/first-mascot
  • Create an animated character/docs/tutorials/first-animation
  • Make a character talk/docs/tutorials/make-it-talk
  • Put your mascot on your website/docs/tutorials/mascot-on-your-website
  • Guide onboarding with a talking mascot/docs/tutorials/web-onboarding-mascot
  • Add a mascot that celebrates progress and welcomes users back/docs/tutorials/web-progress-companion
  • Add a talking support mascot in the bottom right/docs/tutorials/web-support-mascot
  • Show a mascot while your AI thinks/docs/tutorials/ai-loading-mascot
  • Give your AI agent a face/docs/tutorials/ai-agent-face
  • Animate the mascot you already have/docs/tutorials/animate-your-mascot
  • How to make Telegram stickers/docs/tutorials/telegram-stickers
  • How to make Discord stickers/docs/tutorials/discord-stickers
  • Quickstart: your first animation/docs/quickstart
  • Terminal quickstart/docs/terminal
  • API keys and workspaces/docs/authentication
  • CLI browser login/docs/authentication/cli
  • How mascots work/docs/how-mascots-work
  • Credits and costs/docs/credits
  • From text/docs/create/from-text
  • From an image/docs/create/from-image
  • From a website/docs/create/from-website
  • From a brand book/docs/create/from-brand-book
  • Reference images/docs/manage/references
  • What you can generate/docs/generation
  • Pose-to-animation walkthrough/docs/generate/workflow
  • Images and poses/docs/generate/images
  • Animations/docs/generate/animations
  • Talking animations/docs/generate/talking
  • Logos, backgrounds and stickers/docs/generate/design-assets
  • Cursor follower/docs/generate/cursor-follower
  • Batch generation/docs/generate/batch
  • Image and video exports/docs/manage/media-exports
  • Canvases and releases/docs/canvas
  • Build a canvas/docs/canvas/build
  • Canvas templates/docs/canvas/templates
  • Generate canvas assets/docs/canvas/generate-all
  • Export a canvas/docs/canvas/export
  • Overview and demo/docs/player
  • Play one animation/docs/player/one-animation
  • Play a canvas/docs/player/canvas
  • Hosting and file formats/docs/generate/cdn
  • File sizes/docs/generate/file-sizes
  • Float a mascot on your Mac/docs/integrations/desktop
  • Jobs and polling/docs/manage/jobs
  • Webhooks/docs/manage/webhooks
  • Manage mascots and assets/docs/manage/collections
  • All endpoints/docs/reference
  • Requests and responses/docs/reference/conventions
  • Errors and recovery/docs/reference/errors
  • Command line reference/docs/terminal/commands
  • TypeScript SDK/docs/sdk/typescript
  • Changelog/docs/reference/changelog
  • Migrate to mascots and canvases/docs/manage/studio-migration
Get an API key
Documentation

Talking animations

Make your mascot say a line in its own voice, moving as it talks. You get MP4 and transparent WebM and HEVC with sound, the voice as MP3 and a word-timed transcript.

Ko welcoming a new user, from this line:

[waves hello, cheerful] Welcome in! [leans in, whispering] Psst... the big orange button does everything. [points down, proud] That one!

1. Give the mascot a voice

A mascot has one voice; a variant uses the Original's until it gets its own. Get ideas, hear three samples, keep one. Every account can add voices.

Ideas come from the mascot's references and description; refresh=true gives new ones.

curl https://api.masko.ai/v1/mascots/MASCOT_ID/voice/suggestions \
  -H "Authorization: Bearer $MASKO_API_KEY"
{
  "data": {
    "ideas": [
      {
        "title": "Cozy and warm",
        "description": "A round, cozy pond creature with a big belly. Male voice, warm and round, medium-low, smiling while he talks, relaxed and kind, easy natural pace. American accent."
      },
      {
        "title": "Excited little sidekick",
        "description": "An excited little cartoon sidekick. Young male voice, fast and bouncy, high energy, can barely contain his excitement, slightly breathless and giggly. American accent."
      }
    ]
  }
}

Send a description and a direction such as "Older" or "A little grumpy" to get the rewritten text, the changes and new directions. Without direction, only directions come back.

{
  "data": {
    "description": "A round, cozy pond creature with a big belly. Male voice, warm and round, medium-high, smiling while he talks, relaxed and kind, easy natural pace. American accent.",
    "changes": [{ "from": "medium-low", "to": "medium-high" }],
    "directions": ["Younger", "More excited", "Slower", "Drier", "Sleepier"]
  }
}

Three voices from one description reading the same line, for 1 credit. sample_line is optional.

curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/voice/samples \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "An excited little cartoon sidekick. Young male voice, fast and bouncy, high energy, slightly breathless and giggly. American accent."
  }'
{
  "data": {
    "samples": [
      { "id": "5b1c2d8e-8a0f-4c55-9f7e-1d2a3b4c5d61", "url": "https://storage.googleapis.com/...", "duration": 9.6 },
      { "id": "6c2d3e9f-9b10-4d66-a08f-2e3b4c5d6e72", "url": "https://storage.googleapis.com/...", "duration": 8.3 },
      { "id": "7d3e4f0a-ac21-4e77-b190-3f4c5d6e7f83", "url": "https://storage.googleapis.com/...", "duration": 9.3 }
    ],
    "sample_line": "Hi! I'm Gubby. I live in your app, and I love showing people around.",
    "cost_credits": 1,
    "expires_at": "2026-09-30T09:12:00Z"
  }
}

Keep a sample within 24 hours; it replaces the current voice. name is optional.

curl -X PUT https://api.masko.ai/v1/mascots/MASCOT_ID/voice \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sample_id": "7d3e4f0a-ac21-4e77-b190-3f4c5d6e7f83" }'
{
  "data": {
    "object": "voice",
    "name": "Gubby's voice",
    "description": "An excited little cartoon sidekick. Young male voice, fast and bouncy, high energy, slightly breathless and giggly. American accent.",
    "sample_url": "https://storage.googleapis.com/...",
    "source": "original",
    "created_at": "2026-09-29T09:14:00Z"
  }
}
  • GET /v1/mascots/{id}/voice returns the voice or null. source is original, variant or creator (a Marketplace mascot using its creator's voice).
  • A new Original voice also applies to every variant without its own.
  • Variants use the same calls under /v1/mascots/{id}/variants/{variantId}/voice; DELETE there restores the Original's voice.

2. Write the line

Send speech.script with bracketed movements where each starts. Bracketed words are never spoken; a feeling after a comma steers the voice.

[waves hello, excited] Hi! I'm Gubby. [points to the right] The docs are right here!
  • A movement starts on the words after it. With no words after it, it leads into the next one, or plays in the closing silence.
  • Movements happen in place, so leaning in never changes the framing.
  • The voice only says what is written. To hear a laugh, write it: [throws its head back, laughing] Ha ha!.
  • Or send speech.text with an optional speech.direction, such as "excited, points at the docs at the end". Masko writes the movements and returns the script.
  • speech.language takes an ISO 639-1 code such as fr; without it, the language is detected from the words.

Length follows the speech, up to 5 minutes. Longer than 15 seconds, the voice is recorded once and cut at real pauses into 5 to 15 second takes; each starts and ends on the start image, so the mascot returns to that pose at cuts and pauses can grow slightly.

3. Estimate for free

Add dry_run: true to get the script and estimate without charging or generating.

curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "animation",
    "item_id": "ITEM_ID",
    "speech": { "text": "Hi! I'\''m Gubby. Here'\''s how the Masko API works.", "direction": "excited about what he does" },
    "dry_run": true
  }'
{
  "data": {
    "dry_run": true,
    "script": "[waves hello, excited] Hi! I'm Gubby. [opens an arm to the right] Here's how the Masko API works.",
    "estimate": { "speech_seconds": 4.3, "duration": 6, "max_duration": 6, "credits": 12, "too_long": false }
  }
}

4. Generate

Send an animation with speech. It starts from item_id or source_image_asset_id and ends on end_image_asset_id, or its start image; variant_id also picks that variant's voice.

curl -X POST https://api.masko.ai/v1/mascots/MASCOT_ID/generate \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "animation",
    "item_id": "ITEM_ID",
    "end_image_asset_id": "END_IMAGE_ID",
    "speech": {
      "script": "[waves hello, excited] Hi! I'\''m Gubby. [opens an arm to the right] Here'\''s how the Masko API works."
    }
  }'
{
  "data": {
    "job_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
    "status": "pending",
    "type": "animation",
    "animation_model": "standard",
    "item_id": "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d",
    "item_name": "Hi, I'm Gubby",
    "estimated_cost": 12,
    "estimate": { "speech_seconds": 4.3, "duration": 6, "max_duration": 6, "credits": 12, "too_long": false },
    "script": "[waves hello, excited] Hi! I'm Gubby. [opens an arm to the right] Here's how the Masko API works.",
    "asset_ids": {
      "video": "5d6e7f80-91a2-4b3c-8d4e-5f6a7b8c9d0e",
      "webm": "6e7f8091-a2b3-4c4d-9e5f-6a7b8c9d0e1f",
      "hevc": "7f8091a2-b3c4-4d5e-8f6a-7b8c9d0e1f20",
      "audio": "8091a2b3-c4d5-4e6f-9a7b-8c9d0e1f2031",
      "transcript": "91a2b3c4-d5e6-4f7a-8b8c-9d0e1f203142"
    },
    "poll_url": "/api/v1/jobs/3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f?api_version=2026-09-26"
  }
}

It costs 2 credits per second. The request charges for estimate.max_duration, the longest the line could need, and refunds unused seconds once the voice is recorded; the job's cost_credits shows the final price.

Poll https://api.masko.ai/v1/jobs/JOB_ID?api_version=2026-09-26 (jobs and polling) and retry safely with the same Idempotency-Key.

The transcript

Fetch the transcript JSON from the file_url of GET /v1/assets/{id}. Times are seconds from the first video frame, silence included, and lines end at ., ! or ? for captions.

{
  "text": "Hi! I'm Gubby. Here's how the Masko API works.",
  "language": "en",
  "duration": 6,
  "words": [
    { "text": "Hi!", "start": 0.26, "end": 0.6 },
    { "text": "I'm", "start": 0.92, "end": 1.04 },
    { "text": "Gubby.", "start": 1.16, "end": 1.5 },
    { "text": "Here's", "start": 2.04, "end": 2.24 },
    { "text": "how", "start": 2.26, "end": 2.38 },
    { "text": "the", "start": 2.42, "end": 2.5 },
    { "text": "Masko", "start": 2.54, "end": 2.9 },
    { "text": "API", "start": 3.0, "end": 3.46 },
    { "text": "works.", "start": 3.56, "end": 3.9 }
  ],
  "lines": [
    { "text": "Hi! I'm Gubby.", "start": 0.26, "end": 1.5 },
    { "text": "Here's how the Masko API works.", "start": 2.04, "end": 3.9 }
  ],
  "movements": [
    { "text": "waves hello, excited", "start": 0.26, "end": 2.04 },
    { "text": "opens an arm to the right", "start": 2.04, "end": 3.9 }
  ]
}

The audio asset is the voice alone as MP3; video assets report has_audio: true in their metadata.

What a line can do

Tubey, a robot whose screen is his face, shows one ability per clip (his real lines also asked for a pixel mouth).

Whisper, shout and laugh.

[raises one hand beside his screen, whispering] I can whisper a little secret... [throws both fists up, excited] or shout the big news! [shakes with laughter, laughing] Ha ha!

Several languages in one line.

[waves, happy] I speak your language too. [waves with both hands, excited] Bonjour ! ¡Hola! Ciao! [puts a hand on his chest, proud] And my voice was made from my look.

Act with the mascot's own features, such as Tubey's screen.

[his screen shows a big pixel heart, happy] I love this feature. [his screen shows a question mark, curious] Any questions? [his screen shows two smiling eyes, calm] Just ask.

Limits

  • speech needs a start image: item_id or source_image_asset_id.
  • The line sets the length, so speech refuses duration, loop, reverse, auto_reverse, sizes, animation_prompt, image_prompt and the image crops. Talking uses Standard; animation_model: "premium" is refused.
  • A mascot without a voice returns 409 with details.reason: "voice_required", before any charge.
  • A talk longer than 5 minutes returns 400; split it into two animations.
  • A minute of talk is about 12 MB in WebM and 30 MB in HEVC at 720 px; see file sizes.
  • Talking animations cannot be reversed or edited, since the voice would not follow. For a new take, resend the script.
  • generate-batch does not accept speech; send each talking animation to /generate.