Skip to main content
This page explains how to write integration plugins of the kind you see on the Market (OBS scene switcher, Twitch/Spotify control, Discord, hotkey) from scratch.
Integrations are written with T1 (trusted-node) and are not a sandbox — the plugin can access everything your Windows user account can access. Run only code you wrote/reviewed yourself. The public T2 sandbox model is different (see Trust tiers).

Core model: T1 = full Node process

The one idea that matters: evoX runs your T1 plugin as a separate, full Node.js process. That means:
  • You can use any npm library you want (npm install obs-websocket-js ws node-fetch …).
  • You can connect to external services directly — WebSocket, HTTPS API, local port. evoX does not sit in between.
  • The SDK only manages the host side: which event arrived (key pressed, knob rotated), what is shown on the key (title/image/state), settings and logs.
So the division of labor is: external service logic is your Node code + your npm libraries; the key/settings/event bridge is the SDK.

Project setup

Declare the action and its settings in manifest.json. Setting types are rich — secret, oauth, select, color, number, toggle, hotkey, url, path are supported:

Pattern 1 — WebSocket integration (OBS)

runtime.mjs: set up the connection on the first willAppear, send commands on keyDown, clean up on willDisappear.
The ws://127.0.0.1 connection is made directly — T1 is full Node, so no SDK “network capability” is needed. (This is not the case in the public T2 sandbox; there the host-brokered network.fetch is used.)

Pattern 2 — API + OAuth (Twitch/Spotify style)

There are two ways:
  1. Your own token management: T1 is full Node, so you can read the token from a secret setting and use it directly with fetch.
  2. Host-brokered OAuth: With SDK beginOAuth(provider) / getOAuthStatus(provider) you use evoX’s credential vault; the raw token never returns to your plugin, the host injects it into the request.

Key image — setImage (dynamic images such as album art)

Besides the title, you can draw a dynamic image on the key. Two formats:
For multi-state actions, change the state (starting at 0) with setState(id, n).

Knob support

Declare the action with "controllers": ["Knob"] and listen to action.dialRotate / action.dialDown — ideal for continuous values such as volume/brightness.

Lifecycle and cleanup

  • Set up the connection lazily on willAppear; close it on plugin.stop.
  • If polling is needed, start it on willAppear and stop it on willDisappear; use exponential backoff on errors.
  • Wrap every handler in try/catch; log the error with console.error (visible in the Developer window) and notify the user with showError.
  • The host assigns identity: do not write to any context other than event.context.bindingInstanceId; ignore an actionId you don’t recognize.

Next step

Test in your own evoX

Load the project from the Developer window, assign it to a key, reload and see the logs.
References: Events and host-RPC · Manifest · Capability model (T2).