dashboard

Feeds the latest-events widget and chat window. Requires DASHBOARD_EVENTS and/or DASHBOARD_CHAT (see each method).

Platform registration

Call at load time so the UI can resolve platform ids:

await dashboard.registerPlatform({
  id: 'myplatform',
  name: { en: 'My Platform', ru: 'Моя платформа', uk: 'Моя платформа' },
});

Use the same id in addRecord / addChatMessage platform field.

Users

await dashboard.upsertUser({
  id: 'user-1',
  name: 'Viewer',
  avatar: 'https://example.com/avatar.png',
  platform: 'myplatform',
  color: '#7fff00',
  icons: ['badge-vip'], // ids from registerChatBadges
});

Events widget — addRecord

Requires: DASHBOARD_EVENTS

await dashboard.addRecord(
  {
    id: random.id(),
    type: 'donation', // donation | subscribe | follow | custom | timer
    platform: 'myplatform',
    amount: [10, 'USD'],
    message: { en: 'Thanks!', ru: 'Спасибо!' },
    from: 'user-1',
    // Optional addon attaches (after registerAttaches)
    attach: [
      {
        type: 'clip',
        value: { en: 'Funny moment', ru: 'Забавный момент', uk: 'Кумедний момент' },
        id: 'clip-1',
        playable: true,
        playing: false,
      },
    ],
  },
  { id: 'user-1', name: 'Viewer', platform: 'myplatform' },
  { trigger: { type: 'donation', key: 'USD', value: 10 } }, // optional overlay trigger
);

message accepts plain string, { en, ru?, uk? }, or app LangData tuple.

Multiple triggers: { triggers: [...] }.

Attach types — registerAttaches

Requires: DASHBOARD_EVENTS

Register attach types at load time. type must be unique and must not use system ids (overlay, sound, timer, hotkey, coop-sync).

await dashboard.registerAttaches([
  { type: 'clip', label: { en: 'Clip', ru: 'Клип', uk: 'Кліп' } },
]);

Pass matching attach entries in addRecord, or update an existing row:

await dashboard.updateRecordAttaches('record-id', [
  {
    type: 'clip',
    value: { en: 'Funny moment', ru: 'Забавный момент' },
    id: 'clip-1',
    playable: true,
    playing: true,
  },
], { mode: 'merge' }); // or 'replace' to drop this addon's attaches first
Field Description
type Registered attach type
value Display text (string or { en, ru?, uk? })
id Play-button identifier (not required to be unique); falls back to string value / value.en
playable When true, shows a play/stop button
playing When true, the button shows the stop state

Attach play — onAttachPlay

Requires: DASHBOARD_EVENTS

dashboard.onAttachPlay(async ({ id, type, action, record }) => {
  // action: 'play' | 'stop'
  await dashboard.updateRecordAttaches(record.id, [
    {
      type,
      value: { en: 'Funny moment', ru: 'Забавный момент' },
      id,
      playable: true,
      playing: action === 'play',
    },
  ]);
});
dashboard.offAttachPlay();

The payload includes attach id, type, action, the full stored record, plus recordId / timestamps.

Chat — addChatMessage

Requires: DASHBOARD_CHAT

await dashboard.addChatMessage(
  {
    content: 'Hello chat!',
    platform: 'myplatform',
    from: 'user-1',
    color: '_as_user_',
    // Optional: mark as system for other addons (system indicator in chat)
    system: true,
    // Optional: highlight as a streamer mention in the chat window
    mention: true,
    emotes: [{ word: 'Kappa', url: 'https://example.com/kappa.png' }],
    style: {
      color: '#7c4dff',
      header: { en: 'Highlighted', ru: 'Выделено' },
      icon: 'megaphone',
    },
  },
  { id: 'user-1', name: 'Viewer', platform: 'myplatform', color: '#9147ff' },
);

content accepts plain string or { en, ru?, uk? } — not app LangData tuples (addons use their own localized objects).

Optional system: true marks the message as system for addons. Other addons receive it as msg.message.system in onChatMessage. The chat window still renders a normal user line and shows a system indicator after the platform icon.

Optional mention: true marks the message as a streamer mention. Other addons receive it as msg.message.mention in onChatMessage. The chat window highlights the line with an amber accent.

Optional color sets the message text color (not the border). Accepted values:

Value Example Result
Hex with # '#ff0000', '#f00' Normalized to #rrggbb
Hex without # 'ff0000', 'f00' Same
RGB tuple [255, 128, 0] #ff8000
RGB object { r: 0, g: 0, b: 255 } #0000ff
_as_user_ '_as_user_' Same color as the author's nickname (user.color)

Invalid values are ignored. Channels outside 0…255 are clamped.

Optional style adds a colored border and optional header bar:

Field Description
color Border and header background (CSS color, e.g. #ff9800)
header Header text (string or { en, ru?, uk? }); omit for border only
icon exclamation, question, megaphone, or list

System chat — addSystemChatMessage

Requires: DASHBOARD_CHAT

System lines are not from platform users. The UI shows this addon's icon. Optional sender is shown before the message text.

await dashboard.addSystemChatMessage({
  content: { en: 'Connected to chat', ru: 'Подключено к чату' },
  sender: { en: 'My addon' },
  color: '#4caf50',
  style: {
    color: '#4caf50',
    header: { en: 'Notice' },
    icon: 'exclamation',
  },
});

content, sender, and style.header accept string or { en, ru?, uk? } only. Optional color uses the same formats as addChatMessage (hex / RGB / _as_user_).

Chat badges and emotes

await dashboard.registerChatBadges([
  { id: 'badge-vip', url: 'https://example.com/vip.png', title: 'VIP' },
]);

await dashboard.registerChatEmotes({
  platforms: ['myplatform'],
  emotes: [{ word: 'hello', url: 'https://example.com/hello.png' }],
});

When a chat message is saved, StreamKit+ scans its text for registered emotes on that platform and merges matches into message.emotes (entries already passed by the sender win on collisions). Those emotes stay on the stored message and in onChatMessage payloads even after the registering addon is removed.

Read APIs (require DASHBOARD_CHAT or DASHBOARD_CHAT_INCOMING):

Chat send / incoming

Receive composer sends (be a send target): DASHBOARD_CHAT

await dashboard.onChatSend(async ({ text, system }) => {
  // Treat `system` by message purpose (not UI styling):
  // - omitted / false — streamer-authored (chat window input never sets system)
  // - true — system/automatic (e.g. bot reply via a bot account)
  if (system) {
    // send as bot / automated account
  } else {
    // send as the streamer
  }
});
dashboard.offChatSend();

Dispatch outgoing text (same path as the chat window): DASHBOARD_CHAT_SEND

Omit the second argument to send through all registered chat-send subscribers. Pass a string array to target specific addon ids.

Optional third argument { system?: boolean } marks the send purpose for onChatSend handlers. Defaults to false (streamer). Use system: true for automatic/bot messages. The chat window composer never sets system.

// Through every addon that called onChatSend
await dashboard.sendChatMessage('Hello everyone!');

// Through specific addons only
await dashboard.sendChatMessage('Hi Twitch!', ['twitch']);
await dashboard.sendChatMessage('Multi', ['twitch', 'kick']);

// System / automatic send (e.g. bot account)
await dashboard.sendChatMessage('Thanks for the follow!', ['twitch'], {
  system: true,
});

Returns { success: true } when at least one target accepts the message, or { success: false, message } when the text is empty, no valid targets are registered, or every target fails.

Incoming lines: DASHBOARD_CHAT_INCOMING

dashboard.onChatMessage(msg => {
  console.log(msg.message.content, msg.message.system, msg.message.mention, msg.user?.name, msg.sourceAddonId);
});
dashboard.offChatMessage();

Events incoming — onRecord

Requires: DASHBOARD_EVENTS_INCOMING

Subscribe to new records in the latest-events dashboard widget. The payload includes the stored record (with system attach entries for matched overlays, sounds, hotkeys, and timers), resolved user, triggers used for matching, and sourceAddonId.

Unlike chat incoming, the source addon also receives its own records — useful to inspect match results after dashboard.addRecord.

dashboard.onRecord(payload => {
  console.log(payload.record.type, payload.record.attach, payload.triggers);
});
dashboard.offRecord();

Overlay triggers — registerTriggers

Requires: DASHBOARD_EVENTS

Registers event types users can bind to overlays in settings. Pass matching trigger in addRecord options.

await dashboard.registerTriggers([
  {
    type: 'follow',
    label: { en: 'New follower', ru: 'Новый фолловер' },
  },
  {
    type: 'custom',
    key: 'bits',
    label: { en: 'Cheer (bits)' },
    valueType: 'number',
    valueMatch: 'minimum',
    valueHint: { en: 'Minimum bits' },
  },
]);

Trigger option fields

Field Description
type donation, subscribe, subgift, follow, custom
key Fixed discriminator (bits, redeems, …)
label Localized name in overlay settings
valueType text, number, select, dynamic
valueOptions For select
valueProvider For dynamic — handle overlayTriggerValue:{provider}:list|create|release events
valueMatch exact (default) or minimum
keyOptions / keyLabel User-selectable keys (e.g. currency)

Dynamic provider events

events.On('overlayTriggerValue:rewards:list', async () => ({
  success: true,
  items: [{ id: 'abc', label: 'My reward', meta: '100' }],
}));

events.On('overlayTriggerValue:rewards:create', async ({ title, context }) => ({
  success: true,
  valueId: 'abc',
  label: title,
  notify: {
    variant: 'success',
    title: { en: 'Reward created' },
    message: { en: `Cost: ${context?.cost}` },
  },
}));

Responses may include optional notify — a modal in settings (variant: success | error | info; title?, message).

Trigger bindings after settings save

When saved trigger rules for your addon change, the main process fires:

events.On('triggers:applied-changed', ({ previous, current }) => {
  // previous / current group rules by system:
  // overlay, timer, game, gameInput, sounds, hotkeys
});

Use this to release internal resources when bindings are removed.

Validate trigger bindings before settings save

Before the app persists trigger-related settings, it calls each related addon:

events.On('triggers:validate', ({ draft }) => {
  for (const rule of draft.overlay || []) {
    if (rule.trigger.key === 'redeems' && !String(rule.trigger.value || '').trim()) {
      return {
        success: false,
        message: 'Generate or select a channel point reward first',
      };
    }
  }
  return { success: true };
});

Return { success: false, message } to block the save. Omitting the handler (or returning success) allows the save. No extra permission is required.

Query saved trigger bindings — triggers.getApplied()

Any addon can request the current trigger map at any time (no extra permission):

const res = await triggers.getApplied();
if (res.success) {
  const { categories } = res;
  // categories.overlay, categories.timer, categories.game,
  // categories.gameInput, categories.sounds, categories.hotkeys
  // Each is a map: addonId → rules[]
  const twitchOverlay = categories.overlay['twitch'] || [];
  const allHotkeys = categories.hotkeys;
}

Includes bindings for every addon (not only the caller). Keys in each category are dashboard event source addon ids, except gameInput where keys are game addon ids.

Delete this addon's trigger bindings — triggers.removeApplied()

Remove persisted rules where this addon is the dashboard source (and related overlay/game target bindings) without extra permissions:

await triggers.removeApplied(); // every system
await triggers.removeApplied({ systems: ['sounds', 'hotkeys'] });

Unreferenced managed dynamic values are released afterward.

See JSDoc on registerTriggers in generated typings for full contract.