Skip to content

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

FieldTypeDescription
modelTV1ExtModel | undefinedBroadcaster for the current room.
userTV1ExtUser | undefinedCurrent 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:

FieldTypeDescription
guestsnumberNumber of guest (unauthenticated) viewers.
withTokensnumberNumber of viewers who hold tokens.
totalnumberTotal 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:

FieldTypeDescription
settingsunknownThe 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:

FieldTypeDescription
actionNamestringName of the action as declared in the manifest actions array.
paramsRecord<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:

FieldTypeDescription
codenumberHTTP status code returned by the external server.
bodyunknownParsed 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 server

Avoid 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:

FieldTypeDescription
durationMsnumberDuration of the activity reservation in milliseconds. Must be a positive finite number up to 30000 (30 seconds).

Result:

FieldTypeDescription
activityIdstringIdentifier 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:

FieldTypeDescription
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:

FieldTypeDescription
messagestringShort stable error message that identifies the failure type.
dataunknownAdditional 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:

FieldTypeDescription
messagestringShort stable log message that identifies the event.
dataunknownAdditional 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:

FieldTypeRequiredDescription
statusTMovableOverlayStatusNoVisual state of the overlay pill.
messagestringNoShort 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:

FieldTypeDescription
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:

FieldTypeRequiredDescription
dataRecord<string, unknown>YesAny JSON-serializable object to send to every frame.
paymentDataTV1PaymentDataFor users in publicPayment 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:

FieldTypeDescription
dataRecord<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:

FieldTypeDescription
messagestringChat 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'
isAnonymousbooleanIf true, the message is sent without an associated user.
userTV1ExtUser | nullUser 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:

FieldTypeDescription
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.
messagestringText 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:

FieldTypeDescription
tokensAmountnumberAmount of tokens to spend.
tokensSpendDataRecord<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:

FieldTypeDescription
tipMenuTV1TipMenuThe 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:

FieldTypeDescription
settingsunknownPreviously 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 settings

See 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:

FieldTypeDescription
settingsunknownThe settings object to save. Shape is defined by the extension.
isErrorbooleanSet 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:

FieldTypeRequiredDescription
keystringYesKey name declared in the extension manifest.
valuestringYesValue to store.
ttlstringNoAuto-delete the key after this many seconds. Omit or 0 = no expiry.

Result:

FieldTypeDescription
successbooleanWhether 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:

FieldTypeDescription
keystringKey name declared in the extension manifest.

Result:

FieldTypeDescription
existsboolean | undefinedfalse when the key has never been set (value will be '' empty string).
valuestringStored 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:

FieldTypeRequiredDescription
keystringYesKey name declared in the extension manifest.
valuestringYesText to append.
ttlstringNoAuto-delete the key after this many seconds. Omit or 0 = no expiry.

Result:

FieldTypeDescription
successbooleanWhether 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:

FieldTypeDescription
keystringKey name declared in the extension manifest.

Result:

FieldTypeDescription
existsboolean | undefinedfalse when the key has never been set (value will be 0).
valuestringCurrent counter value.

Example

ts
const res = await ext.makeRequest('v1.storage.int.get', {
  key: 'totalVotes',
});
console.log(res.value); // 0 if never set

v1.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:

FieldTypeRequiredDescription
deltastringYesAmount to add (may be negative). Must not be zero.
keystringYesKey name declared in the extension manifest.
paymentContextstringNoOpaque payload forwarded to server-side payment/policy validation.

Result:

FieldTypeRequiredDescription
failureReasonIV1IntIncrementFailureReasonNoPresent when success is false.
successbooleanYesWhether the increment was applied.
valuestringNoNew 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:

FieldTypeDescription
keystringKey name declared in the extension manifest.

Result:

FieldTypeRequiredDescription
successbooleanYesWhether the counter was reset.
valuestringNoStored 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:

FieldTypeRequiredDescription
mutexNamestringYesMutex name declared in the extension manifest.
ttlstringNoAuto-release after this many seconds. Omit or 0 = hold until explicit unlock.

Result:

FieldTypeDescription
successbooleantrue 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:

FieldTypeDescription
mutexNamestringMutex name declared in the extension manifest.

Result:

FieldTypeDescription
successbooleanWhether 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:

FieldTypeDescription
mutexNamestringMutex name declared in the extension manifest.

Result:

FieldTypeDescription
heldbooleantrue 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:

FieldTypeRequiredDescription
keystringYesCounter name declared in the extension manifest.
topTV1TopWindowYesSliding time horizon: 'TOP_1D', 'TOP_7D', or 'TOP_30D'.
topNnumberNoHow many users to return in topUsers. Default 10, max 100.

Result:

FieldTypeRequiredDescription
topUsersTV1TopUserEntry[]NoUp 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:

FieldTypeRequiredDescription
amountstringYesPositive amount to add.
keystringYesCounter name declared in the extension manifest.
metastringNoAn optional string saved for this increment from that user's latest event.
userIdstringYesUser ID credited for this increment (appears in top/get top users).

Result:

FieldTypeRequiredDescription
metastringNoAn optional string saved for this increment from that user's latest event.
successbooleanYesWhether the increment was recorded.
userIdstringNoEcho of request userId; windowStats apply to this user.
windowStatsTV1TopIncrementWindowStat[]NoOne 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:

FieldTypeDescription
keystringCounter name declared in the extension manifest.

Result:

FieldTypeDescription
successbooleanWhether 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:

FieldTypeDescription
ttlstringWall-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:

FieldTypeDescription
paymentTV1PaymentDataPayment data from v1.payment.tokens.spend.succeeded.
ttlstringWall-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:

FieldTypeDescription
sessionTV1Session | nullCurrent 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:

FieldTypeDescription
dataTV1SessionWhisperDataArbitrary extension-defined action payload for the current session.

Result: null

Example

ts
await ext.makeRequest('v1.session.whisper.user', {
  data: {
    action: 'spin',
    value: 5,
  },
});