WebSocket Guide

Real-time WebSocket APIs for notifications, chat, and threat intelligence streaming. Persistent bidirectional connections with automatic reconnection.

3 WebSocket Endpoints

Real-Time WebSocket APIs

Persistent bidirectional connections for live notifications, chat messaging, and threat intelligence streaming. Low latency, event-driven, and production-ready.

0
WebSocket Endpoints
0
Event Types
0
Event Latency
0
Connection Uptime

Overview

OrbVPN provides three WebSocket endpoints across two services. WebSocket connections are persistent, full-duplex channels that allow the server to push events to your client in real time without polling.

Event-Driven Architecture

Subscribe to specific event channels and receive JSON-formatted messages the instant they occur. No polling, no wasted bandwidth.

Automatic Reconnection

SDKs handle reconnection with exponential backoff. Manual implementations should follow the reconnection strategy documented below.

Heartbeat / Keep-Alive

Ping-pong frames keep connections alive through proxies and load balancers. Clients must respond to server pings within 30 seconds.


Connection Flow

Every WebSocket connection follows the same lifecycle, regardless of endpoint.

Client
Server
Connect
Acknowledge
Subscribe
Stream Events
Ping
Pong

Endpoints

1. OrbNET Notifications

Real-time notifications for account events, subscription updates, device status changes, and server alerts.

WebSocketwss://api.orbai.world/ws/notifications

Receive real-time notifications for your OrbVPN account. Requires a valid JWT access token passed as a Bearer token in the initial connection handshake.

Auth: Bearer
Events
notification
Server → Client
subscription_update
Server → Client
device_status
Server → Client
server_alert
Server → Client

Notification Persistence

Notifications are persisted server-side. If your client disconnects, missed notifications are delivered as a batch upon reconnection (up to 100 events, last 24 hours).


2. OrbNET Chat

Real-time messaging for support chat, reseller communication, and in-app messaging.

WebSocketwss://api.orbai.world/ws/chat

Bidirectional chat messaging for OrbVPN support and in-app communication. Requires a valid JWT access token.

Auth: Bearer
Events
message
Bidirectional
typing
Bidirectional
read_receipt
Bidirectional

3. OrbGuard Threat Stream

Live threat intelligence feed for real-time threat detection and security monitoring.

WebSocketwss://guard.orbai.world/ws/threats

Real-time threat intelligence stream from OrbGuard Labs. Requires an API key passed via the X-API-Key header or as a query parameter.

Auth: API-Key
Events
threat_detected
Server → Client
scan_complete
Server → Client
alert
Server → Client

Threat Stream Volume

The OrbGuard threat stream can produce high event volumes (100+ events/minute during active campaigns). Implement buffering and rate-limiting in your consumer to avoid overwhelming your application.


Connection Examples

JavaScript

// OrbNET Notifications WebSocket
const token = 'eyJhbGciOiJIUzI1NiIs...';

const ws = new WebSocket(
  `wss://api.orbai.world/ws/notifications?token=${token}`
);

ws.onopen = () => {
  console.log('Connected to OrbNET notifications');

  // Subscribe to specific channels
  ws.send(JSON.stringify({
    type: 'subscribe',
    channels: ['notification', 'device_status', 'subscription_update'],
  }));
};

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

  switch (data.type) {
    case 'connection_ack':
      console.log(`Session: ${data.sessionId}`);
      break;
    case 'notification':
      console.log(`[Notification] ${data.payload.title}: ${data.payload.message}`);
      break;
    case 'device_status':
      console.log(`[Device] ${data.payload.deviceId}: ${data.payload.status}`);
      break;
    case 'subscription_update':
      console.log(`[Subscription] ${data.payload.event}: ${data.payload.planName}`);
      break;
    case 'server_alert':
      console.log(`[Alert] ${data.payload.severity}: ${data.payload.message}`);
      break;
    default:
      console.log('Unknown event:', data);
  }
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
  console.log(`Connection closed: ${event.code} ${event.reason}`);
  // Implement reconnection logic (see below)
};

Python

import asyncio
import json
import websockets

async def connect_notifications():
    token = "eyJhbGciOiJIUzI1NiIs..."
    uri = f"wss://api.orbai.world/ws/notifications?token={token}"

    async for websocket in websockets.connect(uri):
        try:
            # Subscribe to channels
            await websocket.send(json.dumps({
                "type": "subscribe",
                "channels": ["notification", "device_status"],
            }))

            async for raw_message in websocket:
                event = json.loads(raw_message)

                if event["type"] == "connection_ack":
                    print(f"Connected. Session: {event['sessionId']}")
                elif event["type"] == "notification":
                    payload = event["payload"]
                    print(f"[Notification] {payload['title']}: {payload['message']}")
                elif event["type"] == "device_status":
                    payload = event["payload"]
                    print(f"[Device] {payload['deviceId']}: {payload['status']}")
                elif event["type"] == "ping":
                    await websocket.pong()

        except websockets.ConnectionClosed:
            print("Connection lost. Reconnecting...")
            continue  # websockets.connect handles reconnection

asyncio.run(connect_notifications())

Go

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/url"
    "os"
    "os/signal"
    "time"

    "github.com/gorilla/websocket"
)

type Event struct {
    Type      string          `json:"type"`
    SessionID string          `json:"sessionId,omitempty"`
    Payload   json.RawMessage `json:"payload,omitempty"`
    Timestamp string          `json:"timestamp,omitempty"`
}

func main() {
    token := "eyJhbGciOiJIUzI1NiIs..."
    u := url.URL{
        Scheme:   "wss",
        Host:     "api.orbai.world",
        Path:     "/ws/notifications",
        RawQuery: fmt.Sprintf("token=%s", token),
    }

    conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    if err != nil {
        log.Fatal("Connection failed:", err)
    }
    defer conn.Close()

    // Subscribe to channels
    subscribe := map[string]interface{}{
        "type":     "subscribe",
        "channels": []string{"notification", "device_status", "server_alert"},
    }
    conn.WriteJSON(subscribe)

    // Handle ping/pong
    conn.SetPongHandler(func(appData string) error {
        conn.SetReadDeadline(time.Now().Add(60 * time.Second))
        return nil
    })

    // Graceful shutdown
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    done := make(chan struct{})
    go func() {
        defer close(done)
        for {
            var event Event
            err := conn.ReadJSON(&event)
            if err != nil {
                log.Println("Read error:", err)
                return
            }

            switch event.Type {
            case "connection_ack":
                fmt.Printf("Connected. Session: %s\n", event.SessionID)
            case "notification":
                fmt.Printf("[Notification] %s\n", string(event.Payload))
            case "device_status":
                fmt.Printf("[Device] %s\n", string(event.Payload))
            case "server_alert":
                fmt.Printf("[Alert] %s\n", string(event.Payload))
            }
        }
    }()

    select {
    case <-done:
    case <-interrupt:
        log.Println("Shutting down...")
        conn.WriteMessage(
            websocket.CloseMessage,
            websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""),
        )
        select {
        case <-done:
        case <-time.After(time.Second):
        }
    }
}

OrbGuard Threat Stream Example

Connecting to the OrbGuard threat intelligence stream uses API key authentication instead of JWT.

// OrbGuard Threat Stream WebSocket
const apiKey = 'ogk_live_abc123...';

const ws = new WebSocket(
  `wss://guard.orbai.world/ws/threats?api_key=${apiKey}`
);

ws.onopen = () => {
  console.log('Connected to OrbGuard threat stream');

  // Subscribe to specific threat categories
  ws.send(JSON.stringify({
    type: 'subscribe',
    channels: ['threat_detected', 'alert'],
    filters: {
      severity: ['critical', 'high'],
      categories: ['malware', 'phishing', 'c2'],
    },
  }));
};

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

  if (data.type === 'threat_detected') {
    const threat = data.payload;
    console.log(`[THREAT] ${threat.severity.toUpperCase()}`);
    console.log(`  Indicator: ${threat.indicator}`);
    console.log(`  Type: ${threat.indicatorType}`);
    console.log(`  Category: ${threat.category}`);
    console.log(`  Score: ${threat.score}/100`);
  }

  if (data.type === 'alert') {
    const alert = data.payload;
    console.log(`[ALERT] ${alert.title}`);
    console.log(`  ${alert.message}`);
    console.log(`  Action: ${alert.recommendedAction}`);
  }
};

Reconnection Strategy

Network interruptions are inevitable. Implement exponential backoff with jitter to reconnect gracefully.

class ReconnectingWebSocket {
  constructor(url, options = {}) {
    this.url = url;
    this.maxRetries = options.maxRetries || 10;
    this.baseDelay = options.baseDelay || 1000;   // 1 second
    this.maxDelay = options.maxDelay || 30000;     // 30 seconds
    this.retryCount = 0;
    this.handlers = {};
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('Connected');
      this.retryCount = 0; // Reset on successful connection
      this.handlers.open?.();
    };

    this.ws.onmessage = (event) => {
      this.handlers.message?.(JSON.parse(event.data));
    };

    this.ws.onclose = (event) => {
      if (event.code === 1000) return; // Normal closure

      if (this.retryCount < this.maxRetries) {
        const delay = Math.min(
          this.baseDelay * Math.pow(2, this.retryCount) + Math.random() * 1000,
          this.maxDelay
        );
        console.log(`Reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount + 1})`);
        this.retryCount++;
        setTimeout(() => this.connect(), delay);
      } else {
        console.error('Max reconnection attempts reached');
        this.handlers.maxRetriesReached?.();
      }
    };
  }

  on(event, handler) {
    this.handlers[event] = handler;
    return this;
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }

  close() {
    this.ws.close(1000, 'Client closing');
  }
}

// Usage
const ws = new ReconnectingWebSocket(
  `wss://api.orbai.world/ws/notifications?token=${token}`,
  { maxRetries: 10, baseDelay: 1000 }
);

ws.on('open', () => {
  ws.send({ type: 'subscribe', channels: ['notification'] });
});

ws.on('message', (data) => {
  console.log('Event:', data);
});

Heartbeat / Ping-Pong

The server sends WebSocket ping frames every 30 seconds. Clients must respond with a pong frame. If no pong is received within 30 seconds, the server closes the connection.

ParameterValue
Ping interval30 seconds
Pong timeout30 seconds
Max missed pings1 (connection closed after 1 missed pong)
Client-initiated pingSupported (server will respond with pong)

Browser WebSocket

Most browser WebSocket implementations handle ping-pong frames automatically at the protocol level. You only need to handle this explicitly when using low-level WebSocket libraries in server-side languages.


Event Message Format

All WebSocket events follow a consistent JSON format:

{
  "type": "notification",
  "id": "evt_abc123def456",
  "timestamp": "2026-02-08T12:00:00.000Z",
  "payload": {
    "title": "Subscription Renewed",
    "message": "Your OrbVPN Premium plan has been renewed for 12 months.",
    "category": "billing",
    "priority": "normal",
    "metadata": {
      "planName": "Premium",
      "amount": 99.99,
      "currency": "USD"
    }
  }
}
FieldTypeDescription
typestringEvent type (matches the subscribed channel name)
idstringUnique event identifier for deduplication
timestampstringISO 8601 timestamp of when the event was generated
payloadobjectEvent-specific data (varies by event type)

Connection Limits

Connection Limits

WebSocket connections are limited per account and per endpoint:

  • Notifications: 5 concurrent connections per user
  • Chat: 3 concurrent connections per user
  • Threat Stream: 2 concurrent connections per API key
  • Total per account: 10 concurrent WebSocket connections

Exceeding these limits returns a 4008 Connection Limit Reached close frame. Existing connections are not affected.


Error Close Codes

WebSocket connections may be closed with the following codes:

CodeReasonAction
1000Normal closureNo action needed
1001Server going awayReconnect with backoff
1008Policy violationCheck authentication credentials
1011Internal server errorReconnect with backoff
4001Authentication failedRefresh token and reconnect
4003ForbiddenCheck permissions for the requested channel
4008Connection limit reachedClose an existing connection before reconnecting
4029Rate limitedWait for the duration specified in the close reason, then reconnect

Best Practices

Always Authenticate First

Pass your token in the connection URL or initial message. Never send sensitive data before the connection_ack event.

Subscribe Selectively

Only subscribe to channels you need. Broad subscriptions increase bandwidth and processing overhead.

Deduplicate Events

Use the event ID field for deduplication. On reconnection, you may receive events that were already delivered.

Handle Backpressure

Buffer incoming events and process them asynchronously. Do not block the WebSocket message handler with slow operations.


Go Real-Time

Integrate live notifications, chat, and threat intelligence into your application with OrbVPN's WebSocket APIs. Low latency, high reliability, and simple event-driven architecture.

View SDKs