Skip to content
Masko logomasko
Docs
Documentation

Hosted developer characters

These endpoints support the macOS Masko developer build. They are an additive API extension under development. Check your server's OpenAPI for availability before using them. Production rollout is pending. CLI browser login and API-key authentication are implemented.

A developer character names an existing Masko collection and canvas as namespace/name, for example acme/bob. The prefix supplies a project name during creation, not an authorization boundary or workspace switch. The authenticated session or API key selects the owning workspace. The app and CLI edit the same hosted graph. A frozen runtime version is immutable and addressed as namespace/name@NUMBER.

Authentication and creation

Use a Bearer API key or CLI session access token. The cURL examples below use Authorization: Bearer $MASKO_API_KEY for automation; the CLI and SDK manage saved browser sessions automatically. Public immutable versions can be fetched without authentication. Private versions and all authoring require workspace access. Never embed an authoring key in an app you distribute.

curl -X POST "$MASKO_API_URL/characters" \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: PERSISTED_UUID' \
  -d '{"character":"acme/bob","prompt":"A friendly orange robot"}'

Set MASKO_API_URL=http://localhost:3000/api/v1 locally. The production public base is https://api.masko.ai/v1. Creation returns collection/canvas IDs and app_url on the configured app origin (localhost during local development). New characters start with one initial idle pose and its four-second seamless loop. Gemini suggests the image and loop prompts from the appearance description; creation waits for this text suggestion (bounded to 30 seconds). It starts no image/video jobs and spends no generation credits. Read and edit the prompts with the graph endpoints. Generate reference candidates and select an appearance before generating poses, then review the pose images before generating animations. An unavailable or malformed suggestion returns 502 suggestion_failed; retry with the same creation key. Successful replays preserve later graph edits.

To register an existing app canvas, supply collection_id and canvas_id instead of prompt. Both IDs must be editable by the caller. Linking preserves its existing graph and does not suggest idle prompts. Replaying creation with the same key and body returns the same character; changing fields conflicts.

URL-encode the slash when using a character reference as one path segment: /characters/acme%2Fbob/graph.

Author the graph

GET /characters/{character}/graph returns graph and expected_graph_hash. PUT on the same path accepts that envelope. A stale hash returns 409. Edits retain unchanged media and invalidate affected images, animations and reverse dependencies. Previous asset records and frozen versions remain available.

POST /characters/{character}/proposals returns a graph proposal and the revision it was based on. It does not save a draft or generate images/videos. Use command:add and arguments:["celebrate"], plus either a description or all four prompts: image, loop, enter, exit. Supplied prompts survive AI completion unchanged. Providing all four bypasses AI. An incomplete AI response returns 502 suggestion_failed; an invalid proposed graph returns 422. Neither saves changes. playback:once declares a temporary performance; playback:held declares an ongoing state that can also run temporarily.

Other proposal commands are pose, loop, transition with arguments ["add"|"update", "NAME"], and suggest with a freeform instruction. Pose edits use image_prompt; loops and transitions use animation_prompt. Transitions accept from, to and edge_id. Apply a reviewed proposal through graph PUT.

POST /characters/{character}/references accepts filename and data_base64 for a reference image up to 12 MB. Invalid base64 or unreadable images return 400 invalid_reference. PATCH accepts asset_id to select a completed image from the character's collection. Creating reference candidates through generation does not select one automatically.

Plan before generating

curl -X POST "$MASKO_API_URL/characters/acme%2Fbob/plans" \
  -H "Authorization: Bearer $MASKO_API_KEY" -H 'Content-Type: application/json' \
  -d '{"targets":"images"}'

The response includes plan_id, draft_revision, estimated_credits, expires_at and detailed work. Targets are reference, images, animations or all. References accept count from 1 to 6. Their plan lists reference_asset_ids: the selected identity image, or existing identity uploads when no selection exists. New candidates do not change the selected identity. Animation duration is 4 to 10 seconds. only selects a pose/performance; new_take:true requires only and previews replacement work. Unchanged completed work is reused by default.

Plans create no provider jobs and spend no generation credits. They expire after one day. Keep prompts and asset URLs returned in plans out of diagnostic logs.

curl -X POST "$MASKO_API_URL/runs" \
  -H "Authorization: Bearer $MASKO_API_KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: PERSISTED_UUID' \
  -d '{"plan_id":"PLAN_ID","max_credits":100}'

A 202 response is a durable receipt with run_id, operation key and polling URLs. Persist your UUID before submitting. A repeated exact request returns the same run, even after the original plan expires. A changed request with that key returns 409. Revision changes, expired new plans, insufficient credit caps and a busy canvas also return 409. The credit ceiling is enforced again before billing.

One active developer run is allowed per canvas. Other canvases can run concurrently. Disconnecting a terminal does not cancel accepted work. Recovery dispatches accepted runs whose workflow was not started. No watcher implicitly retries paid work.

The existing canvas generate-all endpoint also accepts an optional max_credits ceiling and UUID Idempotency-Key; clients using the lower-level surface should retain approved_plan_id and the reviewed revision as documented there.

Recover progress and outputs

EndpointResult
GET /plans/{id}Saved plan
GET /runs?character=acme%2Fbob&limit=50&offset=0Authenticated user's authorized run page in the selected workspace
GET /runs/{id}Status, readiness, job errors, reported refunds and resulting version
GET /runs/{id}/events?after=0Up to 200 durable events, next cursor, terminal flag
GET /runs/{id}/assetsHistorical output asset IDs and metadata
GET /assets/{id}Existing asset detail API with fresh download links
POST /runs/{id}/retryExplicit retry with a new UUID and max_credits

Run states are queued, running, preparing, completed, partial_failed and failed. Event sequence is a monotonic cursor. Continue paging until terminal is true; do not infer terminal status from an empty page. Poll every two seconds. Events contain sanitized job state, not provider prompts or credentials. progress events include data.job_id, data.stage, and the canvas node or edge ID when available. Messages distinguish source-video generation, background removal and WebM export, HEVC export for macOS, and completed transparent videos. Stages are deduplicated per job. A source-video-ready event does not mean the mascot is ready: wait for the run's terminal event and readiness: ready. These events appear in CLI logs and both SDK event callbacks.

Reference runs finish with readiness awaiting_reference_selection; image runs finish with awaiting_animations. Complete animation runs wait for media validation and runtime compilation, then return ready and version_ref. Successful assets remain accessible when other jobs fail. Per-job credit allocation can include the orchestration parent and children; do not sum both as a billing total.

Retry retains completed work and plans missing work against the current draft. If a source video exists but transparent media failed, work.media_repair shows exact missing derivatives. Retrying reuses that video through the same repair mechanism as Desktop, with no new video-generation credits. Repair jobs appear in the run and their output assets remain downloadable. A failed repair requires another explicit retry; it does not start an unbounded series of provider calls.

Each job exposes its user-facing error, allocated_credits, reported credits_refunded, and refund_status when the underlying workflow records it. A null refund field means unavailable, not a promise that a refund is pending. Saved source videos remain charged when only their free derivative processing fails.

Review prompt changes before retrying. Neither a plan nor a run deletes historical outputs. Download fresh asset links and retain asset IDs to find them again.

Use the result

POST /characters/{character}/runtime prepares and delivers the current complete canvas. Authenticate with a write-capable API key or CLI session. Send {} for the character's default canvas or {"canvas":"gaming"} for a canvas name (an ID is also accepted). Duplicate names return 409 ambiguous_canvas; unknown or inaccessible canvases return 404. Preparation checks graph and media access in the selected workspace. Missing media returns 409 version_not_ready and never starts generation or spends credits. It does not change Desktop's active build.

The response contains version_id, version_ref, runtime_build_id, content_hash, character, version, visibility: "private", config, and delivery. Identical graph, media, context and canvas reuse the same snapshot, including concurrent requests. A changed graph or selected media produces another snapshot. No client idempotency key is needed. Existing instances keep their loaded snapshot; recreate them to use changes. Signed media URLs are temporary.

curl -X POST 'https://api.masko.ai/v1/characters/acme%2Fbob/runtime' \
  -H "Authorization: Bearer $MASKO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"canvas":"gaming"}'

In the CLI, use masko show acme/bob --canvas gaming --name bob. In Swift, use Masko.mascot("acme/bob", canvas: "gaming"); in TypeScript, use Masko.mascot('acme/bob', { canvas: 'gaming' }). Release is optional.

POST /characters/{character}/versions freezes a complete validated runtime build. It requires an idempotency UUID and accepts visibility:private|public. Keep a release key for the same revision and visibility; changed input conflicts. The CLI chooses a new persisted operation when the graph revision changes. Generation creates private versions automatically; making one public requires this explicit request. GET /characters/{character}/versions/{number} returns native macOS runtime delivery and access metadata. Its optional target accepts only macos. A version must have all required transparent media before it can be used.

In the native CLI: masko show acme/bob@1 --name bob. The runtime downloads hosted videos. In Swift: try await Masko.mascot("acme/bob", version: 1, api: client). The renderer is currently macOS-only.

Existing marketplace mascots

Marketplace slugs such as ko are separate from developer references such as acme/bob@1. The native SDK uses the same delivery route as Desktop: GET /api/marketplace/{slug}/config?target=macos on the app origin, with the configured API key when present. Free mascots load anonymously; paid mascots use the existing ownership checks. A missing entitlement returns 402. A version mismatch is rejected rather than silently loading a different version.

The SDK maps https://api.masko.ai/v1 to the https://masko.ai marketplace origin. A localhost API stays on localhost, including its port. Cached public marketplace manifests are separated by origin. Desktop's /api/marketplace/my-purchases still requires its paired Desktop credential; it is not an API-key catalog endpoint.

A release requires completed source media and transparent WebM/HEVC derivatives. 409 version_not_ready means generation or media preparation must finish before retrying. The server checks derivatives before freezing the immutable snapshot.