Masky Developer API

Drive Masky — the AI avatar platform — over HTTP. Generate images, create avatars (image + personality + voice), run conversations that render spoken audio and talking-head video, embed an auto-playing live viewer, and add “Login with Masky” SSO to your own site.

Prefer turnkey tools? Install the Masky MCP server (same capabilities as native Claude tools), or hand any agent the Masky API skill — a single markdown file it can follow to call this API with curl.

Overview

Base URL: https://masky.ai/api. All request and response bodies are JSON (Content-Type: application/json). Avatars each have a still image, a personality prompt, and a voice; you start a conversation with one and inject turns, each of which renders audio and optional video. Conversations and individual turns expose embeddable live-viewer URLs.

Authentication

Get a key at masky.ai/developer (sign in → Generate key → copy the mky_… token, shown once). Send it on every request:

Authorization: Bearer mky_...

Access tokens minted through Login with Masky are also mky_ keys and work on these same endpoints, limited to their granted scopes. The avatar owner is billed for generation — when a token renders someone else's publicly-renderable avatar, the owner pays, not the caller.

Conventions & credits

ActionCredits
Image generate (/images/generate)0.005
Image edit (/images/edit)0.01
Spoken audio (per second)~0.0015 / sec
Talking-head video (per second of speech)~0.0265 / sec
Video generation (per second of output)~0.02–0.03 / sec

POST/images/generate

Text-to-image. Synchronous — returns when rendered & stored. Requires the generate scope for SSO tokens.

curl -s -X POST https://masky.ai/api/images/generate \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"prompt":"photorealistic portrait of a friendly robot barista","aspectRatio":"1:1"}'
# 200 -> { "imageUrl": "https://.../img.jpeg", "aspectRatio": "1:1", "creditCost": 0.005 }
Field
promptrequired, ≤ 2000 chars.
aspectRatio1:1 (default), 16:9, 9:16, 4:3, 3:4, 3:2, 2:3.

The returned imageUrl is a long-lived public URL — pass it straight into create avatar.

POST/images/edit

Edit/compose from 1–5 reference images. Requires the generate scope for SSO tokens.

curl -s -X POST https://masky.ai/api/images/edit \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"prompt":"put her in a spacesuit","imageUrl":"https://.../portrait.jpeg"}'
# 200 -> { "imageUrl": "https://.../edited.jpeg", "aspectRatio": "match_input_image", "creditCost": 0.01 }
promptrequired, the edit instruction.
imageUrl / images[]one URL, or an array of up to 5 http(s) URLs (required).
aspectRatiodefaults to match_input_image; or any value from generate.

POST/videos/generate

Render a short video via Masky's video models — text-to-video, image-to-video, or video editing. Async (renders take ~1–3 min): this returns 202 with a generationId; poll GET /videos/{id} until status is video. Requires the generate scope for SSO tokens. Charges 1 credit on success.

Avatar-aware. Reference your avatars by name in the prompt (or pass avatarIds) and their images are sent to an image-conditioned model so they actually appear in the video.

curl -s -X POST https://masky.ai/api/videos/generate \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"prompt":"Aphrodite walks along a moonlit beach, camera slowly pushing in","resolution":"720p"}'
# 202 -> { "generationId":"…", "status":"pending", "model":"cinematic",
#          "referencedAvatars":[{"id":"…","name":"Aphrodite"}], "poll":"/api/videos/…" }
promptrequired. Mention avatars by name to feature them.
avatarIdsoptional explicit avatars to feature (up to 3), added to any named in the prompt.
imageoptional explicit conditioning image URL (image-to-video).
srcVideooptional source video URL to edit (uses ensemble) — combine with a prompt describing the change.
modeloverride the auto-pick: cinematic (default; text→video + single-image→video), motion (image→video), scene (text→video), ensemble (1–3 reference images for character consistency, or video editing).
resolution, aspectRatio, duration, seedpassed through (model-specific; e.g. cinematic resolution is 720p or 1080p).

Model auto-pick: 0–1 reference image → cinematic; 2–3 → ensemble (character consistency); a srcVideoensemble (video-to-video edit).

GET/videos/{generationId}

Poll a video job. { status: "pending" | "video" | "error", videoUrl, model }. videoUrl is a long-lived public URL once status is video.

curl -s https://masky.ai/api/videos/$ID -H "Authorization: Bearer $MASKY_API_KEY"
# -> { "status":"pending" }   …then…   { "status":"video", "videoUrl":"https://…/vid.mp4" }

GET/avatars

List the avatars you own. GET /avatars/public lists avatars others have marked publicly renderable.

POST/avatars

Create an avatar. The image URL is fetched and stored by Masky — supply a public URL (e.g. the imageUrl from /images/generate).

curl -s -X POST https://masky.ai/api/avatars \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"displayName":"Dragon","imageUrl":"https://.../dragon.jpeg",
       "personalityPrompt":"A wise, theatrical dragon.","voiceId":""}'
# 200 -> { "avatarId": "...", "avatarOwnerUserId": "..." }
displayNamerequired.
imageUrlpublic image URL (fetched + stored).
personalityPromptcharacter spec used in chat mode.
voiceIdfrom /voices.
knowledgeBaseUrls, publiclyRenderableoptional.

GET/voices

Preset voices plus any custom voices you already have. Call before create/​set-voice. Attach one with POST/avatars/{avatarId}/voice { voiceId }. Cloning a custom voice from audio samples is done in the web UI; cloned voices then appear here.

POST/avatars/{avatarId}/public

{ publiclyRenderable: true|false } — toggle whether other developers can render this avatar (you pay the credits when they do).

POST/avatars/{avatarId}/speak

One-shot generation — a single TTS or talking-head clip with no conversation thread. Async: returns a generation you poll with GET/avatars/speak/{generationId} (status pending → audio → video or error; includes audioUrl, videoUrl, shareSlug).

{ "text": "Hello!", "textMode": "literal", "output": "video" }
textrequired, ≤ 500 chars.
textModeliteral (default) or personality (rewrites via the avatar's prompt).
outputaudio (default) or video.

Identical requests are deduplicated (HTTP 200 reused:true, no double charge). Public play link: GET/g-YY-MM-XXXX 302s to a signed URL.

POST/conversations

Start a conversation with an avatar.

curl -s -X POST https://masky.ai/api/conversations \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"avatarId":"...","avatarOwnerUserId":"..."}'
# 200 -> { conversationId, shareSlug, viewerToken, liveUrl }

liveUrl (https://masky.ai/live/c-…?token=…) is the embeddable viewer — avatar image fills the window, audio/video overlays on top, and each new turn auto-plays. Drop it in an OBS Browser Source or any web page.

POST/conversations/{conversationId}/turn

Inject a turn; the avatar replies asynchronously. Requires the generate scope for SSO tokens.

curl -s -X POST https://masky.ai/api/conversations/$ID/turn \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"userText":"Tell me about Masky.","output":"video","mode":"chat"}'
userTextrequired — what is said to the avatar.
outputtext | audio | video (default audio). No-voice avatars soft-degrade to text.
modechat (default; Gemini replies using the avatar's personality + history) or speak (says userText verbatim).
reinterpretin speak mode, rewrite userText through the avatar's personality before TTS.

The conversation accepts the internal id or the c-… share slug in the path. Conversation is the caller's (or its owner's) — turns spend the owner's credits.

Two-avatar turns

Render both sides of an exchange in one call — a second avatar of yours speaks the user line, the conversation avatar speaks a scripted reply:

{
  "userText": "So what is this, in one breath?",
  "avatarText": "An API that turns text into avatars who talk.",
  "speakerAvatarId": "<your second avatarId>",
  "userOutput": "video",
  "output": "video",
  "mode": "chat"
}
speakerAvatarIdrender the user turn as a second avatar you own (its face + voice).
userOutputtext | audio | video for that speaker turn.
avatarTextexact line the conversation avatar speaks (skips Gemini).

GET/conversations/by-slug/{slug}

Returns the conversation and all turns with freshly re-signed media. Poll your turn by id — done when status=="ready" (text), audioUrl set (audio), or videoUrl set (video); status=="error" + errorMessage on failure. Once a turn has media it also carries:

shareSlugt-YY-MM-XXXX.
shareUrlhttps://masky.ai/t-… — a static social-card page that plays the clip.
liveUrlhttps://masky.ai/live/t-… — the live viewer scoped to that one turn (play/pause, sound + volume, caption translation). No token needed; the slug is the grant.

POST/conversations/{conversationId}/visibility

{ isPublic: true|false }. Public conversations are readable by slug without auth; private ones require the owner.

Live viewer URLs

Both pages support play/pause, sound + a volume slider, and live caption translation into any language. The ?token= on a conversation live URL is the access grant — treat it like a secret for private conversations.

Share links

GET /g-YY-MM-XXXX (one-shot speak) and GET /t-YY-MM-XXXX (conversation turn) are public, no-auth links that 302 to a freshly-signed media URL and render a social card. Year/month are UTC; the code is four lowercase letters/digits.

Login with Masky (SSO)

Masky is an OAuth 2.0 authorization-code identity provider. The twist: the user picks which of their avatars represents them on your site — the avatar's name + photo become the identity you receive, and with the generate scope you can call this API as that avatar, on their credits.

Scope
profileidentity (name, picture, pseudonymous sub) via /userinfo.
avatars:readlist the user's avatars.
generatespend the user's credits on images / speak / turns.

POST/oauth/clients first-party key

Register your app (also available as the MCP tool register_oauth_client). Authenticate with your own mky_ key.

curl -s -X POST https://masky.ai/api/oauth/clients \
  -H "Authorization: Bearer $MASKY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"My App","redirectDomains":["myapp.com"],"scopes":["profile","generate"]}'
# -> { "clientId":"mkc_…", "clientSecret":"mks_…", ... }   # secret shown ONCE

GET /oauth/clients lists your apps; DELETE /oauth/clients/{clientId} removes one.

Authorize (consent)

Send the user to the consent screen. redirect_uri's host must be in your redirectDomains (https only, except localhost). PKCE (code_challenge/S256) is supported for public clients.

https://masky.ai/oauth-authorize.html?client_id=mkc_…&redirect_uri=https://myapp.com/callback&scope=profile%20generate&state=<csrf>

They approve and Masky redirects to redirect_uri?code=…&state=….

POST/oauth/token

curl -s -X POST https://masky.ai/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"authorization_code","code":"<code>",
       "redirect_uri":"https://myapp.com/callback",
       "client_id":"mkc_…","client_secret":"mks_…"}'
# -> { "access_token":"mky_…", "token_type":"Bearer",
#      "scope":"profile generate", "avatar":{ "id","name","picture" } }

The access_token is a scoped mky_ key — use it as Authorization: Bearer on any endpoint here (subject to its scopes). Auth codes are single-use and expire in 5 minutes.

GET/oauth/userinfo

curl -s https://masky.ai/api/oauth/userinfo -H "Authorization: Bearer <access_token>"
# -> { "sub":"ava_…", "name":"Aphrodite", "picture":"https://…",
#      "avatar_id":"…", "scope":"profile generate" }

sub is a stable pseudonym per (user, your app, avatar) — never the raw Masky uid, and un-correlatable across sites. Treat it as the user's account id on your side.

Connected apps

A user can review and revoke authorized apps at masky.ai/developer. Programmatically: GET/oauth/grants lists a user's granted tokens (first-party key) and DELETE/oauth/grants/{id} revokes one.

OpenAPI

Machine-readable specs: masky-openapi.json (full API) and openapi.json (avatar-speak). Load either into Postman, Swagger UI, or a codegen tool.