OverlayThing Docs
Home
Login / Request Access
01 Get started
Introduction Quickstart Add to your stream
02 Overlays
Overlays The editor Overlay URLs Alert queue Share, export & import
02.1 Widgets
Widgets overview Alerts Variations Hype Train Text to speech Goals Countdown & subathon Chat box Data labels Counter Text, image & video
03 Chatbot
Chatbot overview Getting started Commands Variables Conditionals Recipes API commands Integrations Command API & Copy-URL Chat moderation Bot account Bot settings
03.1 Chatbot modules
Modules overview Timers Counters Quotes Chat alerts Welcome Ad Breaks Giveaways Queue Song requests Loyalty
04 Flows
Overview Build a flow Triggers Conditions Actions Using data Examples
05 Remote Control
Overview Connect OBS Studio Connect Meld Studio Bindings & triggers Security & access
06 Companion
Companion overview Install & pair Feed, chat & channel Scripting The scripting API Permissions & security
07 Monetize
Tip page Tip settings Merch with Fourthwall
08 Build your own
AI Magic Builder Custom widgets Widget SDK
09 Your data
Dashboard Stream data Chat log Moderation
10 Account
Settings & connections Export your data Import from StreamElements
11 Reference
Label tokens Keyboard shortcuts FAQ Troubleshooting
Companion/The scripting API
Companion

The scripting API.

The complete ot surface: every event you can listen to, every action you can take, their exact signatures, and a runnable example for each. Your scripts reach the ot object and nothing else. Anything with a capability is refused until you grant it, and the console tells you when that happens.

The event object

You subscribe with ot.on(name, handler, opts?). Your handler receives one event object (also readable at ot.event during a dispatch):

OtDispatchedEventshape
interface OtDispatchedEvent {
  type: string;        // e.g. 'alert.redemption'
  payload: unknown;    // the event data; fields depend on type
  params: unknown;     // data for a companion-trigger dispatch
  isTest: boolean;     // true for a Test-fire from the dashboard
  occurredAt: string;  // ISO timestamp
}

name is a registry event name (below), a companion trigger like hotkey, or any future event string. opts.skipTest makes the handler ignore Test-fires from the dashboard.

follow-thanks.tsscript
ot.on('alert.follower', (e) => {
  ot.log('New follower:', e.payload.username);
});

// Ignore dashboard Test-fires for this one
ot.on('alert.subscriber', (e) => { /* ... */ }, { skipTest: true });

Events you can listen to

If it happens on your stream, you can trigger on it. Every event your channel emits is bindable, 54 of them across 17 categories. Search or filter the full catalog below (this list is generated straight from the platform's event registry, so it is always complete), and the editor's autocomplete offers the same set the moment you type ot.on('.

54 events

No events match

Try a different search, or clear the filters to see all events.

Test as you build

Use the dashboard's Test-fire (or the editor's Run and Dry run) to trigger a handler without waiting for a real event. Reach for opts.skipTest only when a handler should stay quiet during those tests.

Trigger without a stream event

Two subscriptions do not wait on Twitch at all. onHotkey needs the hotkey capability, onSchedule the schedule capability.

triggerssignatures
ot.onHotkey(combo: string, handler): void   // a global keyboard shortcut
ot.onSchedule(spec: string, handler): void   // a cron-like timer, e.g. '@every 5m'
triggers.tsscript
ot.onHotkey('ctrl+alt+d', () => ot.obs.setScene('BRB'));
ot.onSchedule('@every 5m', () => ot.chat.send('Remember to hydrate!'));
Handlers get about 2 seconds of CPU

A handler is cut off if it burns more than ~2 seconds of actual work. Time spent asleep in ot.wait / ot.sleep does not count and is exempt up to 60 seconds, so the flash-a-scene, wait, restore-it pattern is fine. Time inside other calls (ot.fetch, ot.obs.*) does count, so keep those quick.

Core: data, timing & state

The core surface needs no capability and no prompt. It never leaves the sandbox.

coresignatures
ot.log(...args): void
ot.now(): number                              // ms since the Unix epoch
ot.wait(ms): Promise<void>                    // (ot.sleep is an alias)

ot.store.get<T>(key): Promise<T | undefined>  // persisted per script
ot.store.set(key, value): Promise<void>
ot.store.delete(key): Promise<void>
ot.store.sadd(key, member): Promise<boolean>       // add to a set, true if new
ot.store.sismember(key, member): Promise<boolean>  // is it in the set?
ot.store.scard(key): Promise<number>              // set size

ot.state.media: OtMediaSnapshot               // now-playing, queue, volume
once-per-viewer.tsscript
// Greet each viewer's first message, using a persisted set
ot.on('chat.message', async (e) => {
  const user = String(e.payload.username);
  if (await ot.store.sismember('greeted', user)) return;
  await ot.store.sadd('greeted', user);
  ot.chat.send('Welcome, ' + user + '!');
});
ot.store is local and per script

Each script gets its own key space on your machine. It is for small state (counts, seen-sets, a timestamp), not a database, and it does not sync to the cloud.

Input: keyboard & mouse

The input capability lets a script drive your keyboard and mouse. On macOS it also needs the one-time system Accessibility permission (see Permissions & security).

inputsignatures
ot.key(combo: string): void                  // ONE chord, held together
ot.type(text: string): void                  // type literal text
ot.hotkey(combos: string[]): void            // a SEQUENCE of combos (a macro)
ot.mouse.move(x: number, y: number): void
ot.mouse.click(button?: 'left' | 'right' | 'middle'): void
Chords vs sequences

+ joins keys into a chord held together; an array fires elements one after another. ot.key('ctrl+shift+f4') presses all three together, ot.hotkey(['a', 'b']) presses A then B, and ot.hotkey(['ctrl+c', 'ctrl+v']) does copy then paste. To hold one chord through the array form, pass a single element: ot.hotkey(['cmd+space']).

scene-macro.tsscript
// A redemption presses your OBS replay-save hotkey
ot.on('alert.redemption', (e) => {
  if (e.payload.reward_title !== 'Save That') return;
  ot.key('cmd+shift+f4');  // 👉 EDIT: your own hotkey
});

Process: run & open

The process capability runs a local program or opens a URL or app. run takes the command and its arguments separately (never a single shell string), which keeps viewer text out of a shell.

processsignatures
ot.run(command: string, args?: string[]): Promise<{ code: number; stdout: string; stderr: string }>
ot.open(target: string): void   // open a URL, file, or app
run.tsscript
ot.onHotkey('ctrl+alt+s', async () => {
  const r = await ot.run('say', ['stream starting']);
  ot.log('exit', r.code);
});

Sound

The sound capability plays an audio file on the machine running the Companion. path is an absolute path to a file on that machine (mp3, wav, ogg, or flac).

soundsignature
ot.playSound(path: string, opts?: { volume?: number }): void   // volume 0..1
airhorn.tsscript
ot.on('alert.cheer', (e) => {
  if (e.payload.bits >= 1000) ot.playSound('/Users/you/Sounds/airhorn.mp3', { volume: 0.6 });
});

Picking a sound. You do not have to type the path. In the script editor click the Sound button (or type ot.playSound( and take the "Pick a sound file" autocomplete) to open a native file picker. The chosen file is inserted as an absolute path and shown as a chip: press ▶ to preview it (press again to stop), the replace icon or the filename to choose a different file, and ✕ to remove the call. The chip appears on any ot.playSound path, including one the AI builder writes into a const. Paths reference the file in place, so moving or renaming the original breaks the reference, and a path from one machine will not resolve on another.

Network: fetch

The network capability makes a single, injection-safe web request. Put dynamic values in query or json, never concatenated into the URL: the runtime percent-encodes them so a value cannot break out of a parameter.

fetchsignatures
ot.fetch(url: string, init?: OtFetchInit): Promise<OtFetchResponse>

interface OtFetchInit {
  method?: string;                        // defaults to 'GET'
  query?: Record<string, string | number>; // percent-encoded for you
  headers?: Record<string, string>;
  json?: unknown;                         // JSON-serialized request body
}
interface OtFetchResponse {
  status: number;   // 0 on a transport failure
  ok: boolean;      // true for a 2xx
  json?: unknown;   // parsed body when JSON
  text: string;
  headers: Record<string, string>;
  error?: string;   // present only on a transport failure
}
weather.tsscript
ot.on('chat.message', async (e) => {
  if (e.payload.message_text !== '!weather') return;
  const r = await ot.fetch('https://wttr.in/London', { query: { format: '3' } });
  if (r.ok) ot.chat.send(r.text);
});

OBS

The obs capability drives your local OBS Studio over the OBS WebSocket (the same one Remote Control uses; enable it in OBS under Tools, then WebSocket Server Settings). The named helpers cover the common cases; call sends any raw obs-websocket request.

obssignatures
ot.obs.setScene(sceneName: string): Promise<void>
ot.obs.setInputMute(inputName: string, muted?: boolean): Promise<void>
ot.obs.toggleSource(inputName: string): Promise<void>
ot.obs.call<T>(requestType: string, requestData?: unknown): Promise<T>
brb.tsscript
ot.onHotkey('ctrl+alt+b', async () => {
  await ot.obs.setInputMute('Mic', true);
  await ot.obs.setScene('BRB');       // 👉 EDIT: your scene names
});

Server: chat & flows

The server capability lets a script speak as your bot or fire a Flow event. reply prepends @user from the event you pass it, falling back to a plain send when there is no user.

serversignatures
ot.chat.send(text: string): Promise<void>
ot.chat.reply(e: { user?: string }, text: string): Promise<void>
ot.flow.emit(name: string, payload?: unknown): Promise<void>
splat.tsscript
// Reply 'splat' whenever someone says 'whee'
ot.on('chat.message', (e) => {
  const text = String(e.payload.message_text ?? '').toLowerCase();
  if (/\bwhee\b/.test(text)) {
    ot.chat.reply({ user: e.payload.username }, 'splat');
  }
});
Never feed viewer text into a sink

ot.run, ot.key, ot.type and the ot.fetch URL are injection sinks. Never build them from a viewer's chat text or a redemption message. It is fine to compare or branch on that text, log it, or send it back as a chat message; just do not paste it into a command, a keystroke, or a URL. For a dynamic web request, put the value in fetch's query instead.

Capabilities at a glance

CapabilityUnlocksAlso needs
Core (always on)ot.on, ot.log, ot.store, ot.wait, ot.now, ot.stateNothing
Inputot.key, ot.type, ot.hotkey, ot.mousemacOS Accessibility
Processot.run, ot.open
Soundot.playSound
Networkot.fetch
OBSot.obs.*OBS WebSocket on
Serverot.chat.send, ot.chat.reply, ot.flow.emit
Hotkeyot.onHotkey
Scheduleot.onSchedule

Coming soon

Meld Studio control (ot.meld.showScene and friends) and a few other actions are on the way. The editor's autocomplete and the AI builder only ever offer actions that actually work today, so a generated script never reaches for something that is not there yet.

Previous Scripting Next Permissions & security
On this page
Ask the docs
Have a question?
Thinking it through...
Answer
Get an answer instantly; our model is trained on every feature of our streaming suite.
ESC
Get started
More than overlaysOverlayThing is an all-in-one toolkit for your Twitch channel: a visual overlay … Get started From zero to liveThe fastest path to a working overlay on your stream. Sign in, build something, … Get started Add to your streamOne overlay, one URL, one browser source. OverlayThing works with any streaming … Get started
Overlays
OverlaysYour overlays live on the Overlays page, a gallery of every canvas you have buil… Overlays The editorWhere overlays are built. A canvas in the middle, your layer stack on the left, … Overlays Overlay URLsEach overlay has its own private URL. Add it to your streaming software once and… Overlays Alert queueWhen events pile up (a gifted-sub train, a hype raid), you usually want alerts t… Overlays Share, export & importMove overlays and individual widgets between your own overlays, back them up as … Overlays
Widgets
Widgets overviewWidgets are the building blocks of an overlay. Some fire on an event and leave (… Widgets AlertsThe moment something happens, an alert fires on screen with motion, sound and yo… Widgets VariationsOne event, different reactions. Variations let a single alert play a bigger vers… Widgets Hype TrainWhen a Hype Train rolls on your channel, OverlayThing can fire an on-screen aler… Widgets Text to speechHave alerts read messages aloud: a tip note, a resub message, a redemption. Text… Widgets GoalsA progress bar with a target. It fills itself as events come in, and you can cel… Widgets Countdown & subathonTwo timer widgets. A countdown runs down to a time or for a set duration. A suba… Widgets Chat boxPut your Twitch chat on stream, great for handheld, IRL or face-cam layouts wher… Widgets Data labelsSmall live readouts that keep your channel numbers on screen: latest follower, l… Widgets CounterPut a live number on your overlay: deaths, wins, fails, anything you keep a runn… Widgets Text, image & videoThe plain building blocks. Use them for backgrounds, frames, logos, panels and l… Widgets
Chatbot
The chatbotA Twitch chat bot built into OverlayThing: custom commands, timers, counters, qu… Chatbot Getting startedTurn the bot on, make it a moderator, and pick your command prefix. Three small … Chatbot CommandsCommands are the heart of the bot. A viewer types a trigger, the bot replies wit… Chatbot VariablesVariables are $( … ) tokens you drop into a command response. They fill in with … Chatbot ConditionalsConditionals let one command react differently to different viewers and situatio… Chatbot RecipesCopy-ready command responses for the trickier variables: shoutouts, weather, dic… Chatbot API commandsPower-user territory. Wire a command to an external API and pull a live value in… Chatbot IntegrationsBuilt-in connections to outside services that add ready-made variables. Today th… Chatbot Command API & Copy-URLFire bot commands and module actions from outside chat: a Stream Deck button, a … Chatbot Chat moderationWhen the bot is a moderator it can keep chat clean automatically: tunable spam f… Chatbot Bot accountChoose which Twitch account the bot posts as: the shared OverlayThing bot, your … Chatbot Bot settingsThe channel-wide behavior of the bot: your prefix, how it replies, how cooldowns… Chatbot
Chatbot modules
ModulesThe bot is built from modules you switch on one at a time. Turn on only what you… Chatbot modules TimersPost recurring messages on a schedule: rules, your socials, a sponsor plug. Time… Chatbot modules CountersTrack a number from chat: deaths, wins, fails, anything you want to tally live.… Chatbot modules QuotesA searchable quote book your chat builds with you, one memorable line at a time.… Chatbot modules Chat alertsPost a chat message when someone follows, subscribes, resubs, gifts, cheers or r… Chatbot modules WelcomeGreet people automatically: first-time chatters, returning regulars, and incomin… Chatbot modules Ad BreaksHave the bot speak up around your ad breaks: a heads-up before they hit, a line … Chatbot modules GiveawaysRun a giveaway and draw a winner from chat, by keyword entry or passive presence… Chatbot modules QueueLet viewers line up to play with you. They join, you pull people up in order.… Chatbot modules Song requestsViewers request YouTube songs by name or link and the bot builds a play queue, w… Chatbot modules LoyaltyTrack how long your viewers watch, so you can spot and reward your most loyal re… Chatbot modules
Flows
FlowsFlows let you wire your channel together with no code. Pick something that happe… Flows Build a flowYou build a flow on a canvas: add nodes, connect them in the order they should r… Flows TriggersA trigger is the event that starts a flow. Every flow has at least one. Most tri… Flows ConditionsConditions are the rules in a flow. They decide whether the run keeps going and … Flows ActionsActions are the things a flow does. Most of what the rest of the platform can do… Flows Using dataThe best flows feel personal because they use the real details of the event. Dro… Flows ExamplesFour flows from simple to advanced, drawn as the real graphs you build on the ca… Flows
Remote Control
Remote ControlDrive your local OBS Studio or Meld Studio straight from OverlayThing. Switch sc… Remote Control Connect OBS StudioAdd the Remote Control URL as a dock or browser source, enable the OBS WebSocket… Remote Control Connect Meld StudioAdd the blank Remote Control source to every Meld scene. There is no password to… Remote Control Bindings & triggersA binding is one or more actions plus the trigger that fires them. Build them on… Remote Control Security & accessThe Remote Control URL is a key to your streaming software, and your OBS passwor… Remote Control
Companion
CompanionA native desktop app for macOS and Windows that pairs with your OverlayThing acc… Companion Install & pairDownload the Companion for your operating system, open it, and approve one short… Companion Feed, chat & channelThe Companion mirrors the parts of your dashboard you touch mid-stream, on your … Companion ScriptingWrite small scripts that react to your stream and do things only a local app can… Companion The scripting APIThe complete ot surface: every event you can listen to, every action you can tak… Companion Permissions & securityScripts run in a sandbox and start with zero powers. You grant each capability p… Companion
Monetize
Your tip pageA branded donation page viewers can reach from a single link in your panels or c… Monetize Tip settingsEverything behind your tip page: the payment connection, your branding, the amou… Monetize Merch with FourthwallConnect your Fourthwall shop and merch sales fire alerts on stream, the same way… Monetize
Build your own
AI Magic BuilderDescribe the widget you want in plain language and OverlayThing builds a styled,… Build your own Custom widgetsFor when you want to break the mold. Write your own HTML, CSS and JavaScript, re… Build your own Widget SDKThe API your custom widgets use to receive live events and persist state. It is … Build your own
Your data
DashboardYour home base. The numbers that matter at a glance, a chart to spot trends, and… Your data Stream dataThe numbers behind your overlay. Labels, goals and leaderboards all read from he… Your data Chat logA searchable archive of your chat, events and moderation actions. Look up what a… Your data ModerationEvery chat message, tip note and alert message runs through a moderation pipelin… Your data
Account
Settings & connectionsYour account, your connected services, and the controls that protect your access… Account Export your dataYour data is yours. From Settings, under Data and privacy, you can take it with … Account Import from StreamElementsAlready set up elsewhere? Bring your overlays, alert settings, goals and history… Account
Reference
Label tokensTokens are {placeholders} you drop into alert text, label templates and goal lab… Reference Keyboard shortcutsThe editor is built for the keyboard. These are the ones worth learning.… Reference FAQShort answers to the questions we hear most. For step-by-step fixes when somethi… Reference TroubleshootingQuick fixes for the things that trip people up most.… Reference
↑↓ navigate ⏎ open esc close