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)
Message type. One of: authenticate, create_context, ping
Unique ID for authentication context (required for create_context)
Authentication requirements (for create_context)
Authentication type: payment, entrance, membership
Payment amount in cents (for payment type)
Incoming (BOOP → Vendor)
Message type. One of: authenticated, auth_result, error, pong
ID of the authentication context
Authentication result (for auth_result)
Whether authentication succeeded
Requested user attributes
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