Developer The control API: examples
Developer

The control API: examples

JavaScript (Node 22 or later)

Connect on the Mac running InterDirector, follow the state, and cut to whatever is in Preview:

const socket = new WebSocket('ws://127.0.0.1:49721');

socket.addEventListener('open', () => {
  socket.send(JSON.stringify({ type: 'hello', client: 'My Panel', protocol: 2 }));
});

socket.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'state') {
    const name = (id) => message.inputs.find((input) => input.id === id)?.name ?? 'nothing';
    console.log(`Program: ${name(message.program)}, Preview: ${name(message.preview)}`);
  } else if (message.type === 'done') {
    console.log(`Done: ${message.requestID}`);
  } else if (message.type === 'refused') {
    console.log(`Refused (${message.reason}): ${message.requestID ?? ''}`);
  }
});

// Cut whatever is in Preview to Program, five seconds in.
setTimeout(() => {
  socket.send(JSON.stringify({ type: 'action', verb: 'cutPreview', requestID: 'take-1' }));
}, 5000);

// Keep the connection alive.
setInterval(() => socket.send(JSON.stringify({ type: 'ping' })), 10000);

Python (no packages needed)

Read the state and mute an input by name, over HTTP:

import json
import urllib.request

BASE = 'http://127.0.0.1:49721'

def call(path, body=None):
    request = urllib.request.Request(
        BASE + path,
        data=None if body is None else json.dumps(body).encode(),
        headers={'X-Client': 'My Script', 'Content-Type': 'application/json'},
        method='GET' if body is None else 'POST')
    try:
        with urllib.request.urlopen(request, timeout=5) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        return json.load(error)

state = call('/v2/state')
print('Show:', state['show'])
microphone = next(i for i in state['inputs'] if i['name'] == 'Microphone')
print(call('/v2/action', {'verb': 'mute', 'inputID': microphone['id'], 'requestID': 'mute-1'}))

curl

# The state
curl http://127.0.0.1:49721/v2/state

# Start a fade of Preview to Program over two seconds
curl -X POST http://127.0.0.1:49721/v2/action -H 'X-Client: curl' \
  -d '{"verb": "fadePreview", "parameters": {"milliseconds": 2000}}'

# Press a hotkey, from another computer, with the web remote's password
curl -k -X POST https://studio-mac.local:49722/v2/press \
  -H 'Authorization: Bearer <the web remote password>' \
  -d '{"mappingID": "8A1B2C3D-4E5F-4061-9273-8495A6B7C8DA"}'

From another computer, over a WebSocket

Sign in before anything else. InterDirector sends a challenge; answer it with a proof made from the password, then say hello as above. In Node, with the ws package, since the Mac’s certificate is its own and Node’s built-in WebSocket will not accept it:

import { createHmac, pbkdf2Sync } from 'node:crypto';
import WebSocket from 'ws';

const socket = new WebSocket('wss://studio-mac.local:49722/socket', { rejectUnauthorized: false });
const password = '<the web remote password>';

socket.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'challenge') {
    const key = pbkdf2Sync(password, Buffer.from(message.salt, 'base64'), message.iterations, 32, 'sha256');
    const proof = createHmac('sha256', key)
      .update(Buffer.concat([Buffer.from('interdirector-web-remote-sign-in-v1\n'), Buffer.from(message.nonce, 'base64')]))
      .digest('base64');
    socket.send(JSON.stringify({ type: 'signIn', proof }));
  } else if (message.type === 'signedIn') {
    socket.send(JSON.stringify({ type: 'hello', client: 'My Panel', protocol: 2 }));
  }
});

Keep the session from signedIn to resume with {"type": "resume", "session": …} after a dropped connection, within ten minutes.

Contact support

Or write to support@interdirector.com. From InterDirector itself, Help → Report an Issue… sends the logs with your message. See the privacy notice for what the chat keeps.