live.js

<script src="https://devlive.thinkbeyondcorporate.com/sdk/v1/live.js"></script>

Defines window.VelvetLive, named like Lovense Cam Kit's model.js. It runs on the model's page only (it holds her model token). It never sees your secret key.

live.js keeps its own connection to Velvet Live for tips and status, separate from the toy panel. So receiveTip and the events work whether the panel is mounted on this page or open in the model's own tab.

VelvetLive.initCamApi(modelToken)

Start with the token your server minted. Calling it again with a new token (after a rotation) switches everything, including a mounted panel, without the model re-pairing her toy.

VelvetLive.mount(element, options?)

Puts the toy panel into element (an element or a CSS selector) as an iframe with allow="bluetooth". Returns the iframe.

Option
heightCSS height, default 640px
virtualToytest models only: start with the virtual toy (ignored for live tokens)

The token travels in the iframe URL's fragment (/panel/#mt=…), which browsers never send to any server.

VelvetLive.receiveTip(cname, amount, note?)

Lovense Cam Kit's argument order: tipper name, tokens, optional note. Returns a promise:

const r = await VelvetLive.receiveTip('raj_k', 25, '#wave');
// { ok: true, tipId, duplicate: false, action: { kind, strength, durationMs, rule, preset? } }  (strength 0–100, ms)
// { ok: false, error: 'toy_offline' }   no panel open: nothing recorded

Arguments that don't fit (a number as the name, a fraction, 0) are refused with invalid_tip_args, never guessed. (Lovense's Cam Extension uses (amount, name): check your call when you switch.)

Browser tips are unverified: they only move this model's own toy, and they don't count toward reporting. Prefer the server API when your server sees the tip.

VelvetLive.getSettings()

Promise of the menu: { version, levels, specials, perks } (shape).

VelvetLive.getToys()

Promise of [{ id, name, type, status: 'on' | 'off', battery }]: the toy the model's panel has paired (empty when no panel is open).

VelvetLive.addMessageListener(callback)

Every event is { type, detail }. Returns a function that removes the listener (removeMessageListener(callback) works too).

typedetail
message{ code, text }: errors and notices (toy_offline, rate_limited, token_revoked, …)
settingsthe menu, on load and whenever the model edits it
toy{ panelConnected, toys }
tipQueueStatus{ paused, held, queueLength, queuedMs }
tipRunning{ running: true, tipper, tokens, strength, remainingMs } or { running: false }
tipLogVelvet only: each tip's timing (received, queued, command, ack)

VelvetLive.destroyCamApi()

Closes the connection and removes the panel (which stops the toy).