Skip to content
Masko logomasko
Docs
Documentation

Customer mascot access

Your backend decides which mascot each customer can use. It requests a temporary mascot URL from Masko and returns it to the app. The SDK loads the mascot from that URL without a Masko login.

Both SDKs currently display a floating desktop companion on macOS. They do not place the mascot inside your app's window or page layout.

Set up once

In Developers → Applications, create an application and a secret API key. Store the masko_sk_... key in your backend environment. It is shown once and must never be included in your app. No mascot allowlist or publishable key configuration is required for URL loading.

An application secret can request access to eligible original private mascots owned by its personal or team workspace. Owners and admins manage team applications. Imported marketplace mascots, public versions and legacy public CDN media are not supported by this restricted delivery flow.

Prepare the mascot's default canvas once with masko show acme/bob --wait, or finish its generation run. Requesting access only selects existing prepared content. It does not create a snapshot, start generation or spend credits.

Your backend: request a mascot URL

After authenticating your customer and checking their permission, call Masko:

// Server only. Choose the mascot after checking this customer's entitlement.
const response = await fetch(
  'https://api.masko.ai/v1/characters/acme%2Fbob/access',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.MASKO_APPLICATION_SECRET}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ customer_reference: customer.id }),
    signal: AbortSignal.timeout(15000)
  }
);
if (!response.ok) throw new Error('Mascot access unavailable');
const { data } = await response.json();
// Return data.url only to this customer, for example as mascotUrl in your
// existing app configuration response. Use Cache-Control: no-store.

Masko returns { data: { url, expires_at } }. The URL expires after ten minutes. Your backend can use ordinary HTTPS from any language; the authoring Masko.client() requires the macOS CLI and is not a Linux/serverless backend SDK.

By default, Masko selects the latest prepared private version of the character's default canvas. If none exists, it returns 409 mascot_not_ready. To pin a specific release or another prepared canvas, include version: 3 in the body. Every returned URL is pinned internally to one immutable version. Updates do not change an already loaded mascot.

customer_reference is an identifier from your own account system. Never trust client-supplied customer IDs or mascot choices without checking them on your server.

Your app: load the URL

In a macOS Electron main process:

import { Masko } from '@masko/sdk';

// mascotUrl comes from your backend's authenticated response.
const bob = await Masko.mascot(mascotUrl);
await bob.show();

A renderer bundled in Electron's resources is discovered automatically. You can also pass { runtimePath } as the second argument. Bundled or explicitly selected runtimes use an isolated host, so an older CLI host cannot block the customer app. An explicit socketDirectory or MASKO_SOCKET_DIR overrides that isolation and is intended for managed integrations or tests. The SDK prepares the runtime JSON and clips on first show. You can also use const masko = await Masko.connect({ runtimePath }) and then const bob = masko.mascot(mascotUrl) to group several local mascots.

In Swift:

import Masko

// mascotURL is the URL returned by your backend.
let bob = try await Masko.mascot(mascotURL)
try await bob.show()

Swift accepts either a URL or its string representation. The URL overload prepares all native clips before returning. Keep the handle and call dispose() when done. Customers do not install the CLI or sign into Masko. Swift and Electron rendering currently support macOS 14+.

Once prepared, commands and hide/show use local media without another access request. Expiry prevents further authorized downloads; it does not interrupt the loaded mascot. A fresh handle currently needs a valid URL even if video files are cached. Restoring after a completely offline restart is not implemented yet.

URL handling and expiry

This is a bearer access URL backed by a hashed, expiring token record. It packages permission in one SDK argument; it is not a public sharing link or a separately signed JWT. Anyone possessing it can use its download permission before expiry. Do not log it, put it in analytics, or store it in a shared cache.

The URL carries token and expires_at in its fragment, after #. The SDK validates the API origin, extracts the token and sends it in the Authorization header to the fragment-free URL. Redirects are rejected. No token is sent in an HTTP query string or to the shared native renderer. This is an SDK access URL, not a link that logs a user in when opened in a browser.

For a custom REST client, extract the token and remove the fragment before sending:

GET /v1/characters/acme%2Fbob/versions/3
Authorization: Bearer masko_pt_...

The response remains { data: { character, version, visibility, config, delivery } }. The native graph is data.delivery.runtimeGraph; this is the compiled runtime export, not the editable authoring graph. Signed animation download links expire no later than the stored access deadline. Editing the URL's expiry cannot extend server permission.

Issuance is limited to 120 requests/minute/application; delivery to 600/application and 60/token. A 429 response includes Retry-After: 60. If access expires before loading finishes, ask your backend for a new URL and create a fresh handle. Your backend can deny future requests, but already issued URLs last until expiry and already downloaded media remains copyable.

CLI verification

masko login
masko applications create --name FocusBuddy
masko applications key APP_ID --output backend-key.json
masko playback access --file backend-key.json --character acme/bob \
  --customer test-customer --output access.json
masko playback fetch --file access.json --output delivery.json
masko show --access-file access.json --name bob --wait

Files are private and never overwritten. The CLI also accepts masko show MASCOT_URL, but use --access-file to keep tokens out of shell history and process arguments. For local development, pass --api-url http://localhost:3000/api/v1; SDKs accept an explicit apiURL for local delivery.

Compatibility and deployment

Existing /v1/playback-sessions, structured access objects and Masko.playback callbacks remain supported for integrations using explicit versions and renewal callbacks. Existing publishable keys remain valid; they are not needed for the new URL loading path. If a publishable-key header is supplied, it must match the token. Application secrets and tokens cannot authenticate as authoring API keys.

These additions require matching Core and runtime/SDK deployment. The customer-access API and updated SDK/runtime remain pending production release; database preparation alone does not enable the integration.