AfterDocumentation Index
Fetch the complete documentation index at: https://docs.mascot.bot/llms.txt
Use this file to discover all available pages before exploring further.
<MascotProvider> (or LipsyncClient.init()) the SDK runs an
authenticated refresh loop in the background — it keeps the inference engine
licensed without you doing anything. Almost always you ignore this and treat
status === "ready" as the only check you need.
The case worth a page is the rare terminal cutoff. If too many refresh
requests fail in a tight burst (laptop sleep, a long-backgrounded mobile tab,
a real network outage), the session moves to "refused" and stays there —
no automatic retry. Recovery is one call: reload().
The status state machine
useMascot() exposes one value, status:
| Value | Means | Audio work allowed? |
|---|---|---|
idle | Provider hasn’t started initialization yet (e.g. lazy) | No |
initializing | License handshake in flight | No |
ready | Licensed; engine warm | Yes |
running | Currently inferring on a frame | Yes |
degraded | Soft signal from the engine; still usable | Yes |
refused | Terminal cutoff — recover with reload() | No |
error | Init itself failed (bad key, network down at boot) — recover with reload() after fixing the cause | No |
"refused" and "error" are the two states that need active recovery. The
others either advance on their own or simply mean “don’t push audio yet.”
What triggers "refused"
The refresh loop poisons the session after a small burst of consecutive
refresh failures in a short window. The common real-world triggers are all
network-suppression-shaped events:
- A laptop closed mid-session and reopened minutes later.
- A mobile browser tab backgrounded long enough for the OS to throttle timers and pause fetches.
- A device losing connectivity entirely (subway, elevator, plane).
- An aggressive ad-blocker / privacy extension that started intercepting the refresh endpoint.
RefusedError is fired on the client’s
"refused" event. The React Provider listens for it and flips
status → "refused", and any subsequent processAudio() / pushWindow()
throws the same RefusedError.
The canonical recovery pattern (React)
Use both legs together. The pre-check covers “the cutoff already happened, the user is clicking again”; the catch covers “the cutoff fires while this call is in flight.”client.*.
Why reload() instead of “just retry”
reload() tears the client down and runs LipsyncClient.init() again —
a fresh license, a fresh refresh chain, fresh in-memory state. A naive retry
of processAudio() would just hit the same poisoned session. There is no
“un-refuse” API; re-init is the recovery, and that’s what reload() does.
reload() is also the right move for status === "error" once you’ve fixed
the underlying cause (e.g. the user pasted a valid key after seeing
invalid_api_key).
Vanilla equivalent
Outside React, listen on the client and recreate it the same way:"ready" | "refused" | "error" | "refresh"
(API conventions). For
recovery you only need "refused".
Tell the user what happened
ReadinguseMascot().error gives you the typed cause when status is
"refused" or "error". Branch on error.code, not the class — that’s the
stable surface and the full code matrix lives in
Error codes. For RefusedError in particular, the
codes that map to “show a clear next step” UI are listed in that page’s
branching example.
For a plain network/sleep cutoff the user only needs to see “session
expired — tap to continue”; the pre-check inside your action handler already
does the rest on the next tap.
Background tabs and mobile sleep — the practical caveat
Most “the avatar stopped working when I came back” reports are not bugs — they’re the device pausing the refresh loop long enough for the cutoff to fire. Two things make this a non-issue:- The recovery pattern above turns the next tap into a re-init. Users experience one slightly slower click, not a broken page.
- If you can tell when the page becomes visible again (e.g.
document.visibilityState), callreload()proactively so the session is warm before the user interacts. This is optional — the recovery pattern is already correct without it.
Next
Error codes
The full
RefusedError.code matrix and recommended UI per code.React hooks
useMascot — the status / error / reload surface.API conventions
Events vs callbacks, the error taxonomy, module boundaries.