# Using the ServiceHandler

`ServiceHandler` is the helper global object for connecting to the Captivate API server.

#### `init` method <a href="#init-method" id="init-method"></a>

Initializes the connection to the Captivate API server.

```javascript
// when you want the ServiceHandler to automatically determine the best connection method, use this call.
ServiceHandler.init();

// when you want to use a specific websocket url, initialize with a call like this.
ServiceHandler.init(serverUrl);

// when you want to use a pre-existing websocket or any other socket connection (useful when calling from nodejs),
// you may pass the socket as the second argument. The socket must implement at least the `onmessage` and `send`
// methods like the native browser JavaScript WebSocket for sending and receiving messages and for closing connections.
ServiceHandler.init(null, socket);
```

**Arguments**

* `serverUrl` - (optional) URL of the web socket server to use for communication with Captivate. When the script is hosted by the built-in Captivate HTTP server, this argument isn’t needed because Captivate will put its own connection information into a cookie served with the main page. However, if you are hosting your HTML page on another server, you might need to specify this value manually. The url should look like this `ws://CAPTIVATE_COMPUTER_IP:CAPTIVATE_PORT`.
  * `CAPTIVATE_COMPUTER_IP`: The hostname or local IP address of the computer running Captivate.
  * `PORT`: The port Captivate exposes for API communication. Captivate defaults to using port `9023` for the WebSocket API.
    * Changing the webSocket port is possible by editing the NewBlue settings in the Windows Registry or the Mac Preferences.
    * Mac: `defaults write com.newblue.captivate 'Generic HTML.webSocketPort' 10002` (from `~/Library/Preferences/com.newblue.captivate.plist`)
    * Windows RegEdit: `\HKEY_CURRENT_USER\SOFTWARE\NewBlue\Captivate\Generic HTML.httpPort`
    * The main HTTP server port can also be read/set using the `Generic HTML.httpPort` key.
  * To avoid hardcoding this value in your scripts, you can also specify the `host` and `port` using a query string in your browser’s url like this: `?ip=127.0.0.1&port=9023`. The `ServiceHandler` object will automatically look for those values if a `serverUrl` is not provided.
* `socket` - When you are running in nodejs or in some other context, you might have a previously negotiated socket compatible with the JavaScript WebSocket interface. You can pass that socket directly to the `ServiceHandler` as the second argument, specifying the first one as null.

**About the automatic connection method**

When `init` is called with no arguments and the `ServiceHandler` is running in a browser context, the library will attempt to connect with Captivate using a number of different connection methods:

1. `ServiceHandler` will gather some default values from the **URL query parameters**:

* It will use `ip` or `hostName` for the websocket domain (defaults to the domain of the browser url).
* It will use `port` as the websocket port (defaults to `9023`).
* It will use `wsTunnel` as the unique identifier for a remote websocket tunnel (see below).
* Captivate will automatically add these query parameters to any data controller it hosts.

2. `ServiceHandler` will attempt to make the following connections:

* First, it will try to connect to `ws://[ipOrHostName]:[port]` (specified in the url)
* Second, it will try to connect to `ws://127.0.0.1:[port]` (always tries localhost)
* Third, it will try to tunnel its websocket connection through NewBlue’s secure websocket tunnel: `wss://controllers.newbluefx.com/tunnel/[wsTunnel]/client`
* Fourth, if the browser has WebRTC capabilities, `ServiceHandler` will use the NewBlue tunnel to negotiate a **direct secure peer-to-peer** connection to Captivate through WebRTC. If a WebRTC connection can be established, it will replace the websocket tunnel connection resulting in a direct connection that doesn’t pass through any NewBlue servers.

**Example:**

```html
<script src="https://newbluefx.com/api/v3/common/js/qwebchannel.js"></script>
<script src="https://newbluefx.com/api/v3/common/js/servicehandler.js"></script>

<script>
  const cleanups = [];
  const cleanup = () => {
    for (const fn of cleanups) {
      fn();
    }
    cleanups = [];
  };

  // Will attempt to auto-connect.
  ServiceHandler.init();

  ServiceHandler.onready = async () => {
    // The `ServiceHandler.scheduler` object is ready now.
    // Note: webRTC connections take longer to set up, so if one
    // ends up being used, this function might be called a second time.
    // Always make sure to set up your signal connections here.
    cleanups.push(ServiceHandler.scheduler.onNotify.connect(console.log));

    // optional, but you should notify the backend who you are.
    const serverStats = await ServiceHandler.scheduler.notifyClientConnected('My Input Name');

    // serverStats will be an object like this (for more detail, use the `getReport` command)
    // {
    //   "buildDate": "Jan 27 2026",
    //   "buildTime": "09:31:17",
    //   "host": "TitlerLive",
    //   "licensed": "true",
    //   "platform": "macos",
    //   "product": "Captivate",
    //   "sku": "SKUTL5BR",
    //   "version": "2025.0.260127"
    // }
  };

  ServiceHandler.onerror = () => {
    // handle connection failure messages
  };

  ServiceHandler.onclose = () => {
    // handle connection close here
    cleanup();
  };
</script>
```

#### `scheduler` property <a href="#scheduler-property" id="scheduler-property"></a>

Scheduler API object. Will be `undefined` until the `ServiceHandler.onready` callback fires.

**Usage:**

```javascript
// The ServiceHandler.scheduler object will exist as soon as the ServiceHandler object exists
// but calling functions on it will output warnings to the console and resolve to error messages
// until Captivate is connected as if you called a non-existent function
const result = await ServiceHandler.scheduler.getCookies();
// {
// 	"success": false,
// 	"error": "Method not found. Either we have lost connection to Captivate, or the installed version doesn't support this API method."
// }
```

#### Using promises instead of callbacks... <a href="#using-promises-instead-of-callbacks" id="using-promises-instead-of-callbacks"></a>

The `scheduler` object exposes all of its functions in a way that allows callbacks or promises, but they operate differently. The callback method is the older method and will always call the callback with the `string` content of the scheduler response.

To provide a callback to any `scheduler` function, simply include it as the last argument of the function.

However, if a callback function is not provided, the `scheduler` will automatically switch to using the `Promise` method and you can use `await` or `.then()` / `.catch()` functions to access the return value.

Finally, if a function is not exported by the current Captivate API, it will not throw an exception, but will instead respond with a failure message as described above.

```javascript
console.log(await ServiceHandler.scheduler.hello());
/*
{
    "success": false,
    "error": "Method not found. Either we have lost connection to Captivate, or the installed version doesn't support this API method."
}
*/
```

#### `serverUrl` property <a href="#serverurl-property" id="serverurl-property"></a>

Websocket server URL currently used to communicate with Captivate (i.e. `ws://localhost:9023`).

**Usage:**

```javascript
ServiceHandler.onready = () => {
  console.info('ServiceHandler connected to', ServiceHandler.serverUrl);
};
```

#### `onready` callback <a href="#onready-callback" id="onready-callback"></a>

Executes when connection to server is established.

**Usage:**

```javascript
ServiceHandler.onready = () => {
  console.info('ServiceHandler connected');
};
```

#### `onclose` callback <a href="#onclose-callback" id="onclose-callback"></a>

User callback to execute when connection to server is terminated.

**Usage:**

```javascript
ServiceHandler.onclose = () => {
  console.info('ServiceHandler disconnected');
};
```

#### `onerror` callback <a href="#onerror-callback" id="onerror-callback"></a>

User callback to execute when an error is encountered trying to communicate with the server.

**Usage:**

```javascript
ServiceHandler.onerror = (e) => {
  console.error('ServiceHandler error', e);
};
```

## Using the ServiceHandler.scheduler Object

All API actions happen with methods defined on the `scheduler` object. The `scheduler` object directly exposes these functions from the `C++` environment to the JavaScript environment; however, only a few methods are relevant for data controller developers and those are documented here.

In each case, whenever a ‘return’ value is described, it will be passed as the sole argument (as a JSON-encoded string) to a callback function or will be the value of the resolved promise if no callback function is supplied. Additional functions for using the scheduler object can be found on the subsequent pages.
