Skip to main content
Subscribe to real-time desktop events over WebSocket. Monitor window focus changes, clipboard updates, file modifications, process lifecycle, screen resolution changes, and idle/active state — all pushed to your client as they happen.

Connection URL

wss://{computer_id}.orgo.dev/events?token={password}

Authentication

The events WebSocket requires the computer’s password as the token query parameter. Retrieve it from the Get VNC Password endpoint before connecting. Connections without a valid token are rejected with close code 4401.

Query Parameters

token
string
required
Computer password. Retrieve via the Get VNC Password endpoint.

Event Catalog

You can also fetch the event catalog as JSON without a WebSocket connection:
curl https://www.orgo.ai/api/computers/{id}/events

Available Event Types

TypeDescriptionDetection
window_focusActive window changedPolled every 100 ms
window_openNew window openedPolled every 100 ms
window_closeWindow closedPolled every 100 ms
clipboardClipboard content changedPolled every 250 ms
file_changeFile created, modified, or deleted on Desktop or Downloadsinotify (instant)
screen_changeDisplay resolution changedPolled every 1 s
audio_stream_startAudio playback startedPolled every 500 ms
audio_stream_stopAudio playback stoppedPolled every 500 ms
process_startNew process startedPolled every 2 s
process_stopProcess exitedPolled every 2 s
idleNo user input for 30 secondsPolled every 1 s
activeUser input resumed after idlePolled every 1 s

Subscription Model

New connections receive no events until they send a subscribe message. This lets you opt in to only the event types you care about, reducing noise and bandwidth. Subscriptions are:
  • Per-connection — each WebSocket connection has its own subscription set
  • Additive — subscribe to more types at any time
  • Selective — unsubscribe from specific types without disconnecting

Message Protocol

Client → Server Messages

Start receiving specific event types. You can call this multiple times to add more types.
{
  "type": "subscribe",
  "event_types": ["window_focus", "clipboard", "file_change"]
}
type
string
required
Must be "subscribe".
event_types
string[]
required
Array of event type names to subscribe to. See the event catalog for valid types.
Stop receiving specific event types.
{
  "type": "unsubscribe",
  "event_types": ["clipboard"]
}
type
string
required
Must be "unsubscribe".
event_types
string[]
required
Array of event type names to unsubscribe from.
Send a heartbeat ping to keep the connection alive.
{
  "type": "ping"
}

Server → Client Messages

A desktop event matching one of your subscribed types.
{
  "type": "event",
  "event": {
    "type": "window_focus",
    "timestamp": "2026-03-03T12:00:00.123Z",
    "data": {
      "window_id": "0x3200004",
      "title": "Google Chrome"
    }
  }
}
type
string
Always "event".
event
object
The event payload.
event.type
string
Event type name (e.g., "window_focus", "clipboard").
event.timestamp
string
ISO 8601 timestamp of when the event occurred.
event.data
object
Event-specific data. See Event Data Schemas below.
Confirmation that your subscription was updated.
{
  "type": "subscribed",
  "message": "ok"
}
Confirmation that event types were removed from your subscription.
{
  "type": "unsubscribed",
  "message": "ok"
}
Response to a ping message.
{
  "type": "pong"
}
Error message from the server.
{
  "type": "error",
  "message": "Unknown event type: invalid_type"
}
type
string
Always "error".
message
string
Human-readable error description.

Event Data Schemas

Each event type includes a data object with type-specific fields:

Window events

// window_focus
{ "window_id": "0x3200004", "title": "Google Chrome" }

// window_open
{ "window_id": "0x3200008", "title": "Terminal" }

// window_close
{ "window_id": "0x3200008" }

Clipboard

// clipboard
{ "content": "copied text from the clipboard" }

File changes

// file_change
{ "path": "/home/user/Desktop/report.pdf", "action": "created" }
{ "path": "/home/user/Downloads/data.csv", "action": "modified" }
{ "path": "/home/user/Desktop/old.txt", "action": "deleted" }
Monitored directories: /home/user/Desktop and /home/user/Downloads. Detection is via Linux inotify — file events are delivered instantly.

Screen

// screen_change
{ "width": 1920, "height": 1080 }

Audio

// audio_stream_start
{}

// audio_stream_stop
{}

Process lifecycle

// process_start
{ "pid": 1234, "name": "chrome" }

// process_stop
{ "pid": 1234, "name": "chrome" }

Idle / Active

// idle
{ "idle_seconds": 30 }

// active
{}

Examples

const computerId = 'a3bb189e-8bf9-3888-9912-ace4e6543002';
const apiKey = process.env.ORGO_API_KEY;

// Step 1: Get the computer password
const res = await fetch(
  `https://www.orgo.ai/api/computers/${computerId}/vnc-password`,
  { headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const { password } = await res.json();

// Step 2: Connect to events stream
const ws = new WebSocket(
  `wss://${computerId}.orgo.dev/events?token=${password}`
);

ws.onopen = () => {
  // Subscribe to the events you care about
  ws.send(JSON.stringify({
    type: 'subscribe',
    event_types: ['window_focus', 'clipboard', 'file_change', 'idle', 'active']
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case 'event':
      const { type, timestamp, data } = msg.event;
      console.log(`[${timestamp}] ${type}:`, data);

      // React to specific events
      if (type === 'window_focus') {
        console.log(`User switched to: ${data.title}`);
      }
      if (type === 'clipboard') {
        console.log(`Clipboard: ${data.content}`);
      }
      if (type === 'idle') {
        console.log(`Desktop idle for ${data.idle_seconds}s`);
      }
      break;

    case 'subscribed':
      console.log('Subscription confirmed');
      break;

    case 'error':
      console.error('Event error:', msg.message);
      break;
  }
};

// Later: add more subscriptions
ws.send(JSON.stringify({
  type: 'subscribe',
  event_types: ['process_start', 'process_stop']
}));

// Or unsubscribe from some
ws.send(JSON.stringify({
  type: 'unsubscribe',
  event_types: ['idle', 'active']
}));

Use Cases

Agent Awareness

Subscribe to window_focus and idle to give your AI agent context about what the user is doing and when to act.

File Monitoring

Watch file_change events to detect when downloads complete, documents save, or files are created on the Desktop.

Clipboard Sync

Monitor clipboard events to sync clipboard content between the VM and your application in real time.

Process Tracking

Use process_start and process_stop to track application lifecycle — know when Chrome launches, when builds finish, etc.

Best Practices

Subscribe Selectively

Only subscribe to event types you need. This reduces message volume and keeps your handler logic simple.

Heartbeat

Send periodic ping messages (every 30 seconds) to keep the connection alive and detect disconnections early.

Handle Backpressure

Events are dropped for slow consumers (buffer size: 256). Process events quickly or offload to a queue.

Reconnection

Implement automatic reconnection with exponential backoff. Re-send your subscribe message after reconnecting.
Events require the computer to be running. If the computer is stopped, the WebSocket connection will be rejected. After reconnecting, you must re-subscribe — subscriptions are not persisted across connections.