Skip to main content

Overview

The vendor WebSocket endpoint maintains a persistent connection for real-time authentication events and results.
WebSocket connections automatically reconnect if disconnected. Implement exponential backoff for reconnection attempts.

Connection

const ws = new WebSocket('wss://dev.app.boop.it/ws/vendor');

Authentication

Send authentication immediately after connection:
{
  "type": "authenticate",
  "vendor_id": "ven_abc123",
  "api_key": "sk_live_xyz789"
}

Message Types

Outgoing (Vendor → BOOP)

type
string
required
Message type. One of: authenticate, create_context, ping
context_id
string
Unique ID for authentication context (required for create_context)
requirements
object
Authentication requirements (for create_context)

Incoming (BOOP → Vendor)

type
string
required
Message type. One of: authenticated, auth_result, error, pong
context_id
string
ID of the authentication context
result
object
Authentication result (for auth_result)

Message Flow

Example Implementation

const WebSocket = require('ws');

class VendorWebSocket {
  constructor(vendorId, apiKey) {
    this.vendorId = vendorId;
    this.apiKey = apiKey;
    this.contexts = new Map();
  }

  connect() {
    this.ws = new WebSocket('wss://dev.app.boop.it/ws/vendor');

    this.ws.on('open', () => {
      // Authenticate
      this.send({
        type: 'authenticate',
        vendor_id: this.vendorId,
        api_key: this.apiKey
      });
    });

    this.ws.on('message', (data) => {
      const message = JSON.parse(data);
      this.handleMessage(message);
    });

    this.ws.on('close', () => {
      // Reconnect after 5 seconds
      setTimeout(() => this.connect(), 5000);
    });
  }

  createAuthContext(type, requirements) {
    const contextId = crypto.randomUUID();

    this.send({
      type: 'create_context',
      context_id: contextId,
      requirements: {
        type: type,
        ...requirements
      }
    });

    return new Promise((resolve) => {
      this.contexts.set(contextId, resolve);
    });
  }

  handleMessage(message) {
    switch (message.type) {
      case 'authenticated':
        console.log('Connected to BOOP Network');
        break;

      case 'auth_result':
        const resolver = this.contexts.get(message.context_id);
        if (resolver) {
          resolver(message.result);
          this.contexts.delete(message.context_id);
        }
        break;

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

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

// Usage
const vendor = new VendorWebSocket('ven_abc123', 'sk_live_xyz789');
vendor.connect();

// Create payment authentication
const result = await vendor.createAuthContext('payment', {
  amount: 2500,
  attributes: ['email']
});

console.log('Payment authorized:', result.user_id);

Heartbeat

Send periodic ping messages to keep the connection alive:
setInterval(() => {
  ws.send(JSON.stringify({ type: 'ping' }));
}, 30000); // Every 30 seconds

Error Handling

Implement exponential backoff for reconnection:
let retryDelay = 1000;

function reconnect() {
  setTimeout(() => {
    connect();
    retryDelay = Math.min(retryDelay * 2, 30000);
  }, retryDelay);
}
Check your vendor credentials:
{
  "type": "error",
  "error": "Authentication failed",
  "code": "INVALID_CREDENTIALS"
}
Authentication contexts expire after 30 seconds:
{
  "type": "error",
  "error": "Context expired",
  "context_id": "ctx_123"
}

Rate Limits

  • Maximum 100 contexts per minute
  • Maximum 1000 messages per minute
  • Single WebSocket connection per vendor ID