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 --waitFiles 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.