Sign in

WebSocket Realtime

Drive a Cliqer room from your own software like a remote clicker, with slides, the timer, a laser pointer and messages to the host

Connect to wss://cliqer.io/ws to drive a Cliqer room from your own software or show-control system, like a remote clicker: change slides, run the host's timer, point with a laser dot and message the host. Your app joins the room as a presenter; the room itself is always hosted by the Cliqer desktop app.

Auth: connect with a session token: the session_token cookie (browsers send it automatically), a ?token= query parameter or an Authorization: Bearer header. The token is checked when you connect, not on every command, so when it expires open a new connection with a fresh token and join again. Without a token you can connect but cannot use the API. Team API keys (cliqer_...) authenticate the REST endpoints only; on this socket a key is not a session token.

Connection

# WebSocket connections require a WebSocket client
# Using websocat for testing:
websocat "$WS_URL/ws?token=YOUR_ACCESS_TOKEN"

The server sends connected with your clientId once it has set up the connection. Wait for it before you send anything: a message that arrives earlier is rejected with an error and the connection is closed.


Connection & Room Flow

Slide Navigation

Drive the slides in a room from your own software, like a remote clicker. Your app joins the room as a presenter, so the host sees it in the presenter list under its presenterName and controls it like any presenter: the operator can switch its clicks off, remove it or block it.

  • Connect with a session token (?token=, an Authorization: Bearer header or the session_token cookie). Team API keys work on the REST endpoints only, not on this socket.
  • The room owner's plan must include the Client API: the API add-on on Plus, Pro and Max, included on Ultra and Enterprise.
  • Slide and timer commands run only while the host allows clicks from your connection.

Join the room

{ "type": "joinRoom", "payload": { "room": "ABC123", "presenterName": "Show control", "api": true } }
  • Send it after the connected event: the server rejects anything that arrives before it.
  • presenterName is the name the host sees (default API). Your app is always a presenter in the room, never its host.
  • Wait for a clicksEnabled event with payload true before you send commands. The host sends it once it has set up your connection, and again whenever the operator switches your clicks on or off.
  • Keep the presenterKey from joinedRoom and send it in joinRoom when you reconnect, so the host keeps your click permission.
  • Send the text frame ping every 15 seconds to keep the connection open. The server answers with the text frame pong (not JSON).

Send commands

{ "type": "webAction", "payload": { "action": "goto", "slide": 5, "id": "a3" } }
actionExtra fieldWhat it does
nextNext slide (or build)
prevPrevious slide (or build)
firstFirst slide
lastLast slide
gotoslide: whole number from 1 to 9999Go to that slide
blackouton: true or falseBlack the screen, or show it again
  • id is optional: 1 to 64 letters, digits, _, ., : or -. It comes back in the result. Use a new id for every command: the host drops a command whose id it already had from your connection in the last 5 seconds, so a retry of the same command runs once.
  • first, last, goto and blackout run only on a host whose latest controls event lists them in nav. A host that sends no controls event supports next and prev only. With several hosts in the room, each host runs the commands it supports.
  • next or prev while the screen is black shows it again without moving.
  • A goto past the last slide is ignored.
  • Send at most 10 commands per second, slide and timer commands together. The host also drops a repeat of the same command within 50 milliseconds.

What each presentation app supports today:

Presentation appnext, prevfirst, lastgotoblackout
PowerPoint on Windows
Yes
Yes
Yes
Yes
PowerPoint on Mac
Yes
Yes
No
No
Keynote
Yes
No
No
Yes
Google Slides (browser extension connected)
Yes
Yes
No
No
vMix
Yes
No
No
No
Focused window
Yes
No
No
No

The controls event is the live answer for the host you are driving: read nav from it rather than from this table.

# One connection: wait for `connected`, join as an API client, give the host a
# moment to set it up, then send commands. Replies print as they arrive.
( sleep 2
  echo '{"type":"joinRoom","payload":{"room":"ABC123","presenterName":"Show control","api":true}}'
  sleep 3
  echo '{"type":"webAction","payload":{"action":"next","id":"a1"}}'
  sleep 1
  echo '{"type":"webAction","payload":{"action":"prev","id":"a2"}}'
  sleep 1 ) | websocat "$WS_URL/ws?token=YOUR_SESSION_TOKEN"

Results and events

Each command is answered with a webActionResult or an error.

{ "type": "webActionResult", "payload": { "action": "next", "id": "a1", "hosts": 1 } }

hosts is the number of hosts the command was delivered to. The proof that it ran is the host's next slide event; blackout also changes blackout in the host's next controls event. A command the host does not run (clicks switched off, a command it does not support, a goto past the last slide) gets a result but no slide event.

errorWhen
auth_requiredjoinRoom with api: true on a connection without a session token
not_api_clientA command from a connection that has not joined a room with api: true, or after it left
invalid_web_actionUnknown action, or a missing or invalid slide, on or id
invalid_timer_actionUnknown timer action, or a missing or invalid minutes or id
invalid_messageA message without text, or longer than 1000 characters
invalid_presenter_mouseA pointer position outside 0 to 1
rate_limitedMore than 10 slide and timer commands in one second, or more than 5 messages in 10 seconds
no_hostNo host is connected to the room
host_update_requiredEvery host in the room runs an older Cliqer version that cannot take commands from the API
api_not_in_planThe room owner's plan does not include the Client API

Errors carry a readable message and, when they were well-formed, the command's action and id:

{ "type": "error", "error": "invalid_web_action", "payload": { "message": "Unknown or incomplete webAction. Use next, prev, first, last, goto with slide, or blackout with on.", "action": "goto", "id": "a3" } }

Events from a host carry that host's id in hostId:

EventPayload
clicksEnabledtrue when the host takes your commands, false when the operator switched your clicks off
controls{ app, nav, blackout, timer }: the presentation app, the extra slide commands it supports (first, last, goto, blackout), whether the screen is black, and whether the host takes timer commands
slide{ number, current, total, title, notes } after each command the host ran (number is "4/12", total is null when the app does not report it), or null when the host has no slide deck open
countdownThe time the host's timer shows, such as "4:59" ("-0:12" in overtime), every second while it runs, or null when it stops
countdownPhase{ phase, blink }: phase is idle, talk, warning (under a minute left), overtime or paused, and blink is whether the host's timer is flashing
messageFromHostA reply from the host operator, see Messages to the host

You also receive the room's own events (clientJoined, clientLeft and others); ignore the ones you do not use. If the operator removes or blocks your connection you receive logout with reason: "kicked".

Timer

Run the timer in the host's Cliqer app with the same commands a Stream Deck or Bitfocus Companion sends.

{ "type": "timerAction", "payload": { "action": "set", "minutes": 25, "id": "t1" } }
actionWhat it does
startStart the timer, or carry on after stop
stopPause the timer
resetStop the timer and go back to the time it was set to
plus1, plus2, plus5Add 1, 2 or 5 minutes
minus1, minus2, minus5Take off 1, 2 or 5 minutes
preset5, preset10, preset15, preset20Stop the timer and set 5, 10, 15 or 20 minutes
setStop the timer and set minutes, a whole number from 1 to 999
  • Each command is answered with a timerActionResult ({ action, id, hosts }, hosts as for slides) or an error. The proof it ran is the host's next countdown or countdownPhase event.
  • The timer runs only on a host whose latest controls event has timer: true. Hosts on an older Cliqer version do not announce it and ignore timer commands.
  • Timer commands follow the same rules as slide commands: the host runs them only while it allows clicks from your connection, and they share the budget of 10 commands per second.
  • The host's timer settings apply as they are: a timer set to count up counts up, and a timer that follows a connected hardware timer takes its time from that device.

Laser pointer

Show a laser dot on the host's shared screen, the way a presenter's phone does.

{ "type": "presenterMouse", "payload": { "mouse": { "x": 0.5, "y": 0.25 } } }
  • x and y are the position on the shared screen from its top left corner, from 0 to 1.
  • Send { "pressed": false } as the payload to hide the dot. Otherwise it disappears 30 to 90 seconds after your last position.
  • The dot shows only while the host shares a screen and has Laser Pointer switched on. Its colour follows your presenterName.
  • Send at most 30 positions per second. Extra positions are dropped without an error, and there is no result event.

Messages to the host

Send a message to the host operator. It appears in the host's presenter chat, the same as a message from a presenter's phone, and the operator's replies come back as messageFromHost.

# Join as an API client, then send a message. Replies print as they arrive.
( sleep 2
  echo '{"type":"joinRoom","payload":{"room":"ABC123","presenterName":"Show control","api":true}}'
  sleep 3
  echo '{"type":"messageToHosts","payload":{"text":"Two minutes to the break","id":"m1"}}'
  sleep 30 ) | websocat "$WS_URL/ws?token=YOUR_SESSION_TOKEN"

Reply from the host:

{
  "type": "messageFromHost",
  "payload": {
    "fromClientId": "host_abc123",
    "fromPresenterName": "Stage PC",
    "originalPayload": { "text": "Thanks, got it" }
  },
  "hostId": "host_abc123"
}
  • text is 1 to 1000 characters. Send at most 5 messages every 10 seconds.
  • Each message is answered with a messageToHostsResult ({ id, hosts }) or an error.
  • The operator sees the message under your presenterName, and the host shows a notification when its Notifications setting is on.

Event Reference

Outgoing Events (Client → Server)

EventDescription
joinRoomJoin a room as an API client with api: true (see Slide Navigation)
webActionSlide control (see Slide Navigation)
timerActionTimer control (see Timer)
presenterMouseLaser pointer position (see Laser pointer)
messageToHostsSend a message to the host operator (see Messages to the host)
logoutLeave the room; the connection stays open and can join again

Incoming Events (Server → Client)

EventDescription
connectedSent on open with { clientId, userId, isAuthenticated }
joinedRoomSuccessfully joined the room
clientJoinedSomeone joined the room
clientLeftSomeone left the room
webActionResultA webAction was delivered to the room's hosts
timerActionResultA timerAction was delivered to the room's hosts
messageToHostsResultA messageToHosts was delivered to the room's hosts
clicksEnabledWhether a host takes your commands
controlsWhat a host's presentation app and timer support
slideA host's current slide after a command
countdownThe time on a host's timer
countdownPhaseThe phase of a host's timer
messageFromHostA message from the host operator
logoutThe operator removed or blocked your connection (reason: "kicked")
errorError message