Appearance
Requests
Requests are RPC calls sent from your extension to the platform via makeRequest. Each request has a typed params input and a typed result output.
ts
const result = await ext.makeRequest('v1.ext.context.get', null);v1.ext.context.get
Returns the current context for the room — broadcaster info and viewer info. Returns immediately.
Params: null
Result: TV1ExtContext
| Field | Type | Description |
|---|---|---|
model | TV1ExtModel | undefined | Broadcaster for the current room. |
user | TV1ExtUser | undefined | Current user running the extension. |
Example
ts
const ctx = await ext.makeRequest('v1.ext.context.get', null);
if (ctx.user) {
console.log(ctx.user.username);
}TIP
Call this request once on startup, then use v1.ext.context.updated to keep the context up to date.
v1.ext.viewers.get
Returns the current viewer counts for the room, broken down by category. Supported only for show types public, groupShow, and ticketShow. For other show types, returns null.
Params: null
Result: TV1ExtViewers \| null
When result is TV1ExtViewers:
| Field | Type | Description |
|---|---|---|
guests | number | Number of guest (unauthenticated) viewers. |
withTokens | number | Number of viewers who hold tokens. |
total | number | Total number of viewers in the room. |
Example
ts
const viewers = await ext.makeRequest('v1.ext.viewers.get', null);
if (viewers === null) {
console.log('Viewer counts are unavailable for the current show type');
return;
}
console.log('Total viewers:', viewers.total);
console.log('Viewers with tokens:', viewers.withTokens);TIP
Call this request once on startup. If it returns counts, use v1.ext.viewers.updated to keep them up to date while the show type is public, groupShow, or ticketShow.
v1.ext.settings.get
Returns the extension settings that were previously saved by the model via v1.model.ext.settings.set. Returns {} if no settings have been saved yet or if the stored value fails to parse.
Params: null
Result:
| Field | Type | Description |
|---|---|---|
settings | unknown | The settings object. Shape is defined by the extension. |
Example
ts
const res = await ext.makeRequest('v1.ext.settings.get', null);
const settings = res.settings;See Extension Settings for the full settings flow.
v1.ext.actions.call
Triggers a backend action defined in your manifest.json. Stripchat executes the action server-side and returns the response.
Params:
| Field | Type | Description |
|---|---|---|
actionName | string | Name of the action as declared in the manifest actions array. |
params | Record<string, TV1ActionParamValue> | Key-value parameters passed to the action. Used for URL/header placeholders and request body. |
TV1ActionParamValue is a recursive type that accepts string, number, boolean, null, undefined, arrays, and nested objects of these types.
Result:
| Field | Type | Description |
|---|---|---|
code | number | HTTP status code returned by the external server. |
body | unknown | Parsed response body. |
Example
ts
const result = await ext.makeRequest('v1.ext.actions.call', {
actionName: 'stopDevice',
params: {
deviceId: 'dev_456',
token: 'tok_abc',
},
});
console.log(result.code); // 200
console.log(result.body); // response from the external serverAvoid Unconditional Calls from the background
The EXTENSION_SLOT_BACKGROUND slot loads automatically for every viewer — no interaction required. Never call v1.ext.actions.call unconditionally from it. On a popular stream this will send a simultaneous request from thousands of viewers, which can bring your backend down. Always trigger actions in response to a user interaction or a meaningful event. See Backend Actions for details.
Guest Access
By default, actions are not available to guests (unauthenticated users). If a guest calls an action without the flag enabled, the request will be rejected. To allow guests to call an action, set available_for_guests: true on that action in manifest.json. See Backend Actions.
See Backend Actions for manifest configuration and action types.
v1.ext.activity.request
Reserves exclusive access to the extension's shared activity slots (EXTENSION_SLOT_RIGHT_OVERLAY, EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY) for durationMs milliseconds.
Only one extension can hold activity in a given slot at a time. If the slot is currently occupied by another extension, this call waits until it becomes free, then starts the timer and returns. Other extensions sharing the same slots receive v1.ext.activity.busy while your activity is running, and v1.ext.activity.available when it ends.
Use this for short visual feedback, like reactions, a wheel spin, or a quick success indicator. Keep the animation itself shorter than the full activity so users still have time to notice the result.
The reservation is ephemeral: it is not persisted across platform reboots and is not restored for newly joined users.
This request is only meaningful for extensions that use EXTENSION_SLOT_RIGHT_OVERLAY or EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY slots. For long-lived slot ownership and state that must survive reboots and be available to newly joined users, use Session APIs (see Session extension guide and v1.session.* requests).
Use v1.ext.activity.request when you need a short, one-off visual reservation. If the flow must survive reboots, restore for newly joined users, or keep a long-running shared state, use v1.session.* instead.
Params:
| Field | Type | Description |
|---|---|---|
durationMs | number | Duration of the activity reservation in milliseconds. Must be a positive finite number up to 30000 (30 seconds). |
Result:
| Field | Type | Description |
|---|---|---|
activityId | string | Identifier of the started activity. |
Example
ts
const ACTIVITY_DURATION = 10_000;
const ANIMATION_DURATION = 8_000;
// Request the slot for 10 seconds.
// Resolves only after the slot is free and the timer starts.
const { activityId } = await ext.makeRequest('v1.ext.activity.request', {
durationMs: ACTIVITY_DURATION,
});
// Do your animation / overlay work here.
// The slot is exclusively yours until durationMs elapses.v1.ext.activity.status
Returns whether the activity slots used by this extension are currently free or occupied by another extension. Returns immediately.
Use this on page load to sync the initial UI state before subscribing to v1.ext.activity.busy / v1.ext.activity.available.
Params: null
Result:
| Field | Type | Description |
|---|---|---|
status | 'available' | 'busy' | 'available' — the shared slot is free; 'busy' — another extension currently holds the slot. |
Example
ts
const { status } = await ext.makeRequest('v1.ext.activity.status', null);
if (status === 'busy') {
showWaitingIndicator();
}
ext.subscribe('v1.ext.activity.available', () => {
hideWaitingIndicator();
});v1.monitoring.report.error
Reports an extension-side error to the platform monitoring pipeline (Kibana). The message and data are logged as an error-level entry tagged with the extension ID. Use this for meaningful failures that break initialization, block a main user flow, or force the extension into degraded behavior.
For broader guidance and examples, see Monitoring.
Params:
| Field | Type | Description |
|---|---|---|
message | string | Short stable error message that identifies the failure type. |
data | unknown | Additional structured context for debugging. Prefer JSON-serializable objects. |
Result: null
Example
ts
await ext.makeRequest('v1.monitoring.report.error', {
message: 'extension bootstrap failed',
data: {
stage: 'bootstrap',
reason: 'missing required configuration',
},
});v1.monitoring.report.log
Reports an extension-side diagnostic log to the platform monitoring pipeline (Kibana). The message and data are logged as an info-level entry tagged with the extension ID. Use this for important lifecycle events or fallback decisions that help explain extension behavior in production.
For broader guidance and examples, see Monitoring.
Params:
| Field | Type | Description |
|---|---|---|
message | string | Short stable log message that identifies the event. |
data | unknown | Additional structured context for debugging. Prefer JSON-serializable objects. |
Result: null
Example
ts
await ext.makeRequest('v1.monitoring.report.log', {
message: 'fallback mode enabled',
data: {
stage: 'bootstrap',
feature: 'realtime-sync',
},
});v1.ext.movableOverlay.update
Updates the status and optional message of the EXTENSION_SLOT_MOVEABLE_OVERLAY slot. This is a small floating indicator visible to the user — use this request to reflect the current state of your extension.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
status | TMovableOverlayStatus | No | Visual state of the overlay pill. |
message | string | No | Short text displayed inside the pill. |
Result: null
Example
ts
await ext.makeRequest('v1.ext.movableOverlay.update', {
status: 'active',
message: 'Connected',
});v1.ext.signup.open
Opens the Stripchat sign-up modal. Use this when your extension requires an authenticated account. The modal is shown immediately and the request returns null — there is no callback when the user completes or dismisses sign-up; check context.user afterwards via v1.ext.context.updated.
Params:
| Field | Type | Description |
|---|---|---|
type | 'user' | Sign-up type. Currently only 'user' is supported. |
Result: null
Example
ts
await ext.makeRequest('v1.ext.signup.open', { type: 'user' });v1.ext.whisper
Broadcasts a message through the server to all clients in the room that have this extension loaded. Every slot iframe of every user receives the corresponding v1.ext.whispered event. The server validates paymentData when it is required (users in public shows must supply it; the model may omit it).
Params:
| Field | Type | Required | Description |
|---|---|---|---|
data | Record<string, unknown> | Yes | Any JSON-serializable object to send to every frame. |
paymentData | TV1PaymentData | For users in public | Payment data received from the payment.tokens.spend.succeeded event. This field is required for user in public. |
Result: null
Example
ts
await ext.makeRequest('v1.ext.whisper', {
data: {
type: 'GAME_STARTED',
round: 1,
},
});See Slots Communication for detailed usage and patterns.
v1.ext.whisper.local
Sends a message to other iframes of the same extension within the current browser tab only. Other users never receive it.
Params:
| Field | Type | Description |
|---|---|---|
data | Record<string, unknown> | Any JSON-serializable object. |
Result: null
Example
ts
await ext.makeRequest('v1.ext.whisper.local', {
data: {
type: 'UPDATE_UI',
payload: { score: 42 },
},
});See Slots Communication for when to use broadcast vs local whispers.
v1.chat.message.send
Use this method to notify a user in chat about an event related to the extension, for example showing the result of a game. The message is sent locally to each user on behalf of the extension. The message is not sent to the server and is not broadcast — each user sees it injected into their own local chat feed.
Because the message is local only, it is not persisted: it will disappear from the chat history after the page is reloaded.
Extension link is always appended
Every message sent via this method will have a clickable link to the extension appended at the end of the text. Clicking it opens the extension that sent the message. This behavior is mandatory and cannot be disabled.
Params:
| Field | Type | Description |
|---|---|---|
message | string | Chat message text. Supports inline markup tags: {#accent}…{/accent} highlights text with an accent color, {#fade}…{/fade} renders text in a dimmed/secondary style. Example: 'won {#accent}The Gift{/accent} in' |
isAnonymous | boolean | If true, the message is sent without an associated user. |
user | TV1ExtUser | null | User to attribute the message to. Pass null for system-style messages. |
Result: null
Example
If you call v1.chat.message.send directly, only the user on that specific client will see the message. To show a message in chat for all users, broadcast it first via v1.ext.whisper and then call v1.chat.message.send inside the v1.ext.whispered handler — this way every client renders it locally.
Whisper is free for models, paid for users
When the extension runs on the model's side and calls v1.ext.whisper, no payment is required. When the extension runs on a user's side and calls it, paymentData obtained from the v1.payment.tokens.spend.succeeded event must be included — the server will reject the broadcast otherwise.
ts
// --- Extension on the model side: broadcast for free ---
await ext.makeRequest('v1.ext.whisper', {
// `data` is a free-form object —
// put any JSON-serializable fields you need
data: {
type: 'CHAT_MESSAGE',
message: `won {#accent}${priceLabel}{/accent} in`,
},
});
// --- Extension on the user side ---
// paymentData from v1.payment.tokens.spend.succeeded is required
ext.subscribe(
'v1.payment.tokens.spend.succeeded',
async ({ paymentData }) => {
await ext.makeRequest('v1.ext.whisper', {
paymentData,
// `data` is a free-form object —
// put any JSON-serializable fields you need
data: {
type: 'CHAT_MESSAGE',
message: `won {#accent}${priceLabel}{/accent} in`,
},
});
},
);
// --- Every client: render the message locally in chat ---
ext.subscribe('v1.ext.whispered', ({ data }) => {
if (data.type === 'CHAT_MESSAGE') {
ext.makeRequest('v1.chat.message.send', {
message: data.message,
isAnonymous: true,
user: null,
});
}
});v1.chatbot.message.send
Use this method to send a message in the chat as a bot or model. The message is sent locally to each user and looks like a normal chat message. The message is not persisted and will disappear from the chat after the page is reloaded.
Params:
| Field | Type | Description |
|---|---|---|
from | 'bot' | 'model' | Sender of the message. If bot, the message will be sent with the name of the extension. If model, the message will be sent with the name of the current model. |
message | string | Text of the message. The message can contain {username} placeholder, which will be replaced with the name of the user receiving the message. If the user is anonymous, the placeholder will be replaced with empty string. If the user is guest, the placeholder will be replaced with "Guest". Example usage: "Hello, {username}! Welcome to the chat!" |
Result: null
Example
ts
await ext.makeRequest('v1.chatbot.message.send', {
from: 'bot',
message: 'Welcome to the game!',
});v1.payment.tokens.spend
Opens a token-spend confirmation modal for the current user. The request returns null as soon as the modal opens — the actual spend is async. Listen to v1.payment.tokens.spend.succeeded in every iframe to know when the user confirms and the tokens are deducted.
Params:
| Field | Type | Description |
|---|---|---|
tokensAmount | number | Amount of tokens to spend. |
tokensSpendData | Record<string, unknown> | Custom data which will be passed back in the v1.payment.tokens.spend.succeeded event. |
Result: null
Example
ts
await ext.makeRequest('v1.payment.tokens.spend', {
tokensAmount: 10,
tokensSpendData: {
action: 'spin_wheel',
round: 3,
},
});
ext.subscribe('v1.payment.tokens.spend.succeeded', (data) => {
console.log('Amount:', data.paymentData.amount);
console.log(data.tokensSpendData); // { action: 'spin_wheel', round: 3 }
});v1.tipMenu.get
Get the current tip menu. Tip menu data might change during the broadcast, so subscribe to v1.tipMenu.updated to listen to these changes.
Params: null
Result:
| Field | Type | Description |
|---|---|---|
tipMenu | TV1TipMenu | The current tip menu data. |
Example
ts
const res = await ext.makeRequest('v1.tipMenu.get', null);
if (res.tipMenu.isEnabled) {
res.tipMenu.items.forEach((item) => {
console.log(`${item.activity}: ${item.price} tokens`);
});
}v1.private.request
Programmatically triggers the same flow as the user clicking the Start Private Show button. Stripchat takes over from there — no further extension action is required.
Params: null
Result: null
Example
ts
await ext.makeRequest('v1.private.request', null);v1.model.ext.settings.get
Fetches the model's saved settings for this extension. Use this in your settings.html iframe to populate the form with previously saved values. Distinct from v1.ext.settings.get, which returns the same data already available to all other slots.
Params: null
Result:
| Field | Type | Description |
|---|---|---|
settings | unknown | Previously saved settings object, or a default value if nothing was saved yet. |
Example
ts
const res = await ext.makeRequest('v1.model.ext.settings.get', null);
const settings = res.settings;
// populate form fields with settingsSee Extension Settings for the full settings flow.
v1.model.ext.settings.set
Saves extension settings from the settings page. Call this in response to the v1.model.ext.settings.set.requested event, which fires when the model clicks Save. If isError is true, Stripchat marks the save as failed and keeps the settings modal open so the model can correct errors. If isError is false, the settings are saved and the modal closes on success.
Params:
| Field | Type | Description |
|---|---|---|
settings | unknown | The settings object to save. Shape is defined by the extension. |
isError | boolean | Set to true if validation failed. Stripchat keeps the settings modal open so the model can fix errors. |
Result: null
Example
ts
ext.subscribe('v1.model.ext.settings.set.requested', async () => {
const settings = { theme: 'neon', rounds: 5 };
const isError = false;
await ext.makeRequest('v1.model.ext.settings.set', {
settings,
isError,
});
});See Extension Settings for the full settings flow.
v1.storage.string.set
Store a string value by key. Optionally set a TTL so the key auto-deletes after a given number of seconds.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Key name declared in the extension manifest. |
value | string | Yes | Value to store. |
ttl | string | No | Auto-delete the key after this many seconds. Omit or 0 = no expiry. |
Result:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the value was stored. |
Example
ts
await ext.makeRequest('v1.storage.string.set', {
key: 'name',
value: 'Alice',
});v1.storage.string.get
Read a string value from storage by key.
Params:
| Field | Type | Description |
|---|---|---|
key | string | Key name declared in the extension manifest. |
Result:
| Field | Type | Description |
|---|---|---|
exists | boolean | undefined | false when the key has never been set (value will be '' empty string). |
value | string | Stored string value. Empty string if the key was never set. |
Example
ts
const res = await ext.makeRequest('v1.storage.string.get', {
key: 'name',
});
console.log(res.value);v1.storage.string.append
Append text to an existing string value. If the key does not exist yet, behaves like a set.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Key name declared in the extension manifest. |
value | string | Yes | Text to append. |
ttl | string | No | Auto-delete the key after this many seconds. Omit or 0 = no expiry. |
Result:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the value was appended. |
Example
ts
await ext.makeRequest('v1.storage.string.append', {
key: 'chatLog',
value: 'alice: hello!\n',
});v1.storage.int.get
Read an integer counter value from storage.
Params:
| Field | Type | Description |
|---|---|---|
key | string | Key name declared in the extension manifest. |
Result:
| Field | Type | Description |
|---|---|---|
exists | boolean | undefined | false when the key has never been set (value will be 0). |
value | string | Current counter value. |
Example
ts
const res = await ext.makeRequest('v1.storage.int.get', {
key: 'totalVotes',
});
console.log(res.value); // 0 if never setv1.storage.int.increment
Add delta to an integer counter. Negative delta subtracts. When the key is configured with a payment policy, pass paymentContext so the server can validate the transaction before applying the change.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
delta | string | Yes | Amount to add (may be negative). Must not be zero. |
key | string | Yes | Key name declared in the extension manifest. |
paymentContext | string | No | Opaque payload forwarded to server-side payment/policy validation. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
failureReason | IV1IntIncrementFailureReason | No | Present when success is false. |
success | boolean | Yes | Whether the increment was applied. |
value | string | No | New counter value after the increment (present when success is true). |
Example
ts
const res = await ext.makeRequest('v1.storage.int.increment', {
key: 'totalVotes',
delta: '1',
});
if (res.success) {
console.log('New total:', res.value);
}v1.storage.int.reset
Reset an integer counter to 0.
Params:
| Field | Type | Description |
|---|---|---|
key | string | Key name declared in the extension manifest. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
success | boolean | Yes | Whether the counter was reset. |
value | string | No | Stored value after reset; always 0 when success is true. |
Example
ts
await ext.makeRequest('v1.storage.int.reset', {
key: 'totalVotes',
});v1.storage.mutex.lock
Acquire an exclusive lock on a mutex declared in the extension manifest. Use this to prevent concurrent modifications to shared state.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
mutexName | string | Yes | Mutex name declared in the extension manifest. |
ttl | string | No | Auto-release after this many seconds. Omit or 0 = hold until explicit unlock. |
Result:
| Field | Type | Description |
|---|---|---|
success | boolean | true if the lock was acquired, false if already held. |
Example
ts
const res = await ext.makeRequest('v1.storage.mutex.lock', {
mutexName: 'gameRound',
ttl: '30',
});
if (res.success) {
// safe to modify shared state
}v1.storage.mutex.unlock
Release a previously acquired mutex.
Params:
| Field | Type | Description |
|---|---|---|
mutexName | string | Mutex name declared in the extension manifest. |
Result:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the mutex was released. |
Example
ts
await ext.makeRequest('v1.storage.mutex.unlock', {
mutexName: 'gameRound',
});v1.storage.mutex.getState
Check whether a mutex is currently locked.
Params:
| Field | Type | Description |
|---|---|---|
mutexName | string | Mutex name declared in the extension manifest. |
Result:
| Field | Type | Description |
|---|---|---|
held | boolean | true if the mutex is currently locked. |
Example
ts
const res = await ext.makeRequest('v1.storage.mutex.getState', {
mutexName: 'gameRound',
});
if (res.held) {
console.log('Game round is in progress');
}v1.storage.top.get
Read a top counter: total sum in the sliding window, the last incrementer, top-N users ranked by total, and a per-day breakdown.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Counter name declared in the extension manifest. |
top | TV1TopWindow | Yes | Sliding time horizon: 'TOP_1D', 'TOP_7D', or 'TOP_30D'. |
topN | number | No | How many users to return in topUsers. Default 10, max 100. |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
topUsers | TV1TopUserEntry[] | No | Up to topN users with highest total in the window. |
Example
ts
const res = await ext.makeRequest('v1.storage.top.get', {
key: 'tips',
top: 'TOP_7D',
topN: 5,
});
if (res.topUsers) {
res.topUsers.forEach((u) => {
console.log(`User ${u.userId}: ${u.total}`);
});
}v1.storage.top.increment
Append an increment to a top counter for the current model session. Records the caller's user ID for leaderboard ranking.
Params:
| Field | Type | Required | Description |
|---|---|---|---|
amount | string | Yes | Positive amount to add. |
key | string | Yes | Counter name declared in the extension manifest. |
meta | string | No | An optional string saved for this increment from that user's latest event. |
userId | string | Yes | User ID credited for this increment (appears in top/get top users). |
Result:
| Field | Type | Required | Description |
|---|---|---|---|
meta | string | No | An optional string saved for this increment from that user's latest event. |
success | boolean | Yes | Whether the increment was recorded. |
userId | string | No | Echo of request userId; windowStats apply to this user. |
windowStats | TV1TopIncrementWindowStat[] | No | One entry per standard horizon (1d, 7d, 30d) with that user's total in each window after the increment. |
Example
ts
const res = await ext.makeRequest('v1.storage.top.increment', {
key: 'tips',
amount: '25',
userId: '42',
});
if (res.success && res.windowStats) {
for (const stat of res.windowStats) {
console.log(`${stat.window}: ${stat.value}`);
}
}v1.storage.top.reset
Clear all increments for a top counter key.
Params:
| Field | Type | Description |
|---|---|---|
key | string | Counter name declared in the extension manifest. |
Result:
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the counter was cleared. |
Example
ts
await ext.makeRequest('v1.storage.top.reset', {
key: 'tips',
});v1.session.join.model
Model-only: start a new long-term session. Only one session can be active per model at a time. If no session is active, this call creates one. If a session is already active, this call does not recreate or replace it until that session finishes. The active session TTL is defined by whoever created it first (model or user). Returns nothing (null). The updated session state is delivered immediately via v1.session.updated.
Params:
| Field | Type | Description |
|---|---|---|
ttl | string | Wall-clock session lifetime in seconds (minimum 1). |
Result: null
Example
ts
await ext.makeRequest('v1.session.join.model', {
ttl: '3600',
});v1.session.join.user
User-only: join the long-term session after a successful token payment. If no session is active, this call can create a new one; if a session is already active, it joins that session and does not recreate it. Session identity is stream-scoped and unique per model while active. The active session TTL is defined by whoever created it first (model or user). Use v1.session.get to detect whether the active session is owned by your extension before enabling Start/Join actions in UI. Returns nothing (null). The updated session state is delivered immediately via v1.session.updated.
Params:
| Field | Type | Description |
|---|---|---|
payment | TV1PaymentData | Payment data from v1.payment.tokens.spend.succeeded. |
ttl | string | Wall-clock session lifetime in seconds (minimum 1). |
Result: null
Example
ts
await ext.makeRequest('v1.session.join.user', {
payment: {
amount: '10',
paymentToken: 'pay_tok_123',
transactionId: 'trx_456',
userId: '42',
},
ttl: '3600',
});v1.session.get
Returns the current long-term session.
Params: null
Result:
| Field | Type | Description |
|---|---|---|
session | TV1Session | null | Current session snapshot. |
Example
ts
const res = await ext.makeRequest('v1.session.get', null);
if (res.session?.owner === 'self') {
console.log('Active session:', res.session.meta.sessionId);
}v1.session.finish.model
Model-only: clear the current long-term session. Returns null on success.
Params: null
Result: null
Example
ts
await ext.makeRequest('v1.session.finish.model', null);v1.session.whisper.user
User-only: send a custom action payload within the current long-term session. The payload is delivered to the broadcaster's extension via v1.session.whispered.model (model client only), not broadcast to all viewers. The model should validate the action, update state, then notify viewers through room mechanisms such as v1.ext.whisper.
Params:
| Field | Type | Description |
|---|---|---|
data | TV1SessionWhisperData | Arbitrary extension-defined action payload for the current session. |
Result: null
Example
ts
await ext.makeRequest('v1.session.whisper.user', {
data: {
action: 'spin',
value: 5,
},
});