Skip to main content

Overview

This guide covers how to establish a WebSocket connection, authenticate your POS client, and maintain a healthy connection with proper heartbeat and reconnection handling.
1

Authenticate

Connect with your API key via headers or query parameters
2

Establish Connection

Open WebSocket connection to the API endpoint
3

Implement Heartbeat

Send periodic ping messages to maintain connection health
4

Handle Messages

Process responses and push notifications

Prerequisites

Before connecting, ensure you have:

API Credentials

API key provided by Modulus Labs for your POS integration

WebSocket Client

A WebSocket client library for your programming language

Network Access

Outbound access to the WebSocket endpoint over port 443

JSON Parser

Ability to serialize and parse JSON messages

WebSocket Endpoint

Connect to the WebSocket endpoint:
wss://{your-api-endpoint}/v1
Replace {your-api-endpoint} with your provisioned API endpoint. Contact support@moduluslabs.io to obtain your endpoint URL and credentials.

Authentication

WebSocket connections authenticate during the connection handshake. Provide your API key via headers or query parameters.

Authentication via Headers

Include the x-api-key header in your WebSocket connection request:
HeaderRequiredDescription
x-api-keyYesYour API key
x-group-idNoGroup identifier (optional)

Authentication via Query Parameter

Alternatively, pass your API key as a query parameter:
wss://{your-api-endpoint}/v1?x-api-key=your-api-key
When using query parameters, your API key may appear in server logs. Use header-based authentication in production when possible.

Establishing a Connection

const WebSocket = require('ws');

const API_KEY = process.env.MODULUS_API_KEY;
const WS_ENDPOINT = 'wss://{your-api-endpoint}/v1';

function connect() {
  const ws = new WebSocket(WS_ENDPOINT, {
    headers: {
      'x-api-key': API_KEY
    }
  });

  ws.on('open', () => {
    console.log('Connected to WebSocket API');
    startHeartbeat(ws);
  });

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

  ws.on('close', (code, reason) => {
    console.log(`Connection closed: ${code} - ${reason}`);
    // Implement reconnection logic
    setTimeout(() => connect(), 5000);
  });

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

  return ws;
}

function handleMessage(message) {
  switch (message.action) {
    case 'terminalsResponse':
      console.log('Terminals:', message.terminals);
      break;
    case 'paymentComplete':
      console.log('Payment complete:', message.paymentResponse);
      break;
    case 'terminalStatusUpdate':
      console.log('Terminal status:', message.status);
      break;
    case 'forceDisconnect':
      console.log('Disconnected:', message.reason);
      break;
    case 'pong':
      console.log('Heartbeat received');
      break;
    default:
      console.log('Unknown message:', message);
  }
}

const ws = connect();

Quick Start Flow

1

Connect to WebSocket Endpoint

Establish a WebSocket connection with your API key:
wss://{your-api-endpoint}/v1?x-api-key=your-api-key
2

Discover Available Terminals

Request a list of connected terminals:
{ "action": "getTerminals" }
Receive terminal list:
{
  "action": "terminalsResponse",
  "terminals": [
    {
      "deviceId": "TERM-001",
      "status": "online"
    }
  ]
}
3

Initiate Payment

Send a payment request to a terminal:
{
  "action": "payment",
  "terminalId": "TERM-001",
  "paymentRequest": {
    "transactionId": "TXN-20240115-001",
    "amount": "99.99",
    "currency": "USD",
    "paymentMethod": "CARD"
  }
}
4

Receive Payment Result

The terminal processes the payment and returns the result via push notification:
{
  "action": "paymentComplete",
  "terminalId": "TERM-001",
  "paymentResponse": {
    "transactionId": "TXN-20240115-001",
    "status": "SUCCESS",
    "amount": "99.99",
    "authorizationCode": "AUTH123456"
  }
}

Heartbeat / Keep-Alive

Maintain connection health with periodic ping/pong messages to prevent timeouts and detect stale connections.
SettingValue
Ping interval30 seconds
Pong timeout10 seconds
Reconnect on failureYes

Implementation

let pingTimeout;
let heartbeatInterval;

function startHeartbeat(ws) {
  heartbeatInterval = setInterval(() => {
    ws.send(JSON.stringify({ action: 'ping' }));

    // Set timeout for pong response
    pingTimeout = setTimeout(() => {
      console.log('Pong timeout - connection may be dead');
      ws.terminate();
    }, 10000);
  }, 30000);
}

function handleMessage(message) {
  if (message.action === 'pong') {
    // Clear timeout when pong received
    clearTimeout(pingTimeout);
    console.log('Heartbeat OK');
  }
}

function stopHeartbeat() {
  clearInterval(heartbeatInterval);
  clearTimeout(pingTimeout);
}

Testing Your Connection

1

Connect to the WebSocket endpoint

Verify successful connection by checking for the open event
2

Send getTerminals action

Verify you receive a terminalsResponse with available terminals
3

Test heartbeat

Send a ping and verify you receive a pong response
4

Test reconnection

Simulate a connection drop and verify automatic reconnection works

Quick Connection Test

const WebSocket = require('ws');

async function testConnection() {
  const ws = new WebSocket('wss://{your-api-endpoint}/v1', {
    headers: { 'x-api-key': process.env.MODULUS_API_KEY }
  });

  ws.on('open', () => {
    console.log('1. Connection established');

    // Test getTerminals
    ws.send(JSON.stringify({ action: 'getTerminals' }));

    // Test ping
    setTimeout(() => {
      ws.send(JSON.stringify({ action: 'ping' }));
    }, 1000);
  });

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

    if (message.action === 'terminalsResponse') {
      console.log('2. Terminals received:', message.terminals.length);
    }

    if (message.action === 'pong') {
      console.log('3. Heartbeat working');
      console.log('All tests passed!');
      ws.close();
    }
  });

  ws.on('error', (err) => {
    console.error('Test failed:', err.message);
  });
}

testConnection();

Troubleshooting

Possible causes:
  • Invalid or missing API key
  • API key not authorized for WebSocket access
  • Network firewall blocking WebSocket connections
Solutions:
  • Verify your API key is correct
  • Contact support@moduluslabs.io to verify API key permissions
  • Check that outbound WebSocket connections are allowed on port 443
Possible causes:
  • Heartbeat not implemented
  • Network instability
  • Firewall or proxy terminating idle connections
Solutions:
  • Implement heartbeat with 30-second ping interval
  • Check network connectivity
  • Configure proxy/firewall to allow persistent WebSocket connections
Possible causes:
  • Multiple POS instances using the same API key
  • Previous connection not properly closed
Solutions:
  • Ensure only one POS instance uses each API key
  • Request additional API keys for multiple terminals
  • Implement proper connection cleanup on application exit
Possible causes:
  • Message handler not processing all action types
  • JSON parsing errors
  • Connection silently dropped
Solutions:
  • Log all incoming messages for debugging
  • Add try/catch around JSON parsing
  • Implement heartbeat to detect stale connections

Security Best Practices

Protect API Keys

Store API keys in environment variables, not in source code. Never expose keys in client-side JavaScript.

Use Headers

Prefer header-based authentication over query parameters to avoid keys appearing in logs.

Validate Messages

Always validate incoming message structure before processing to prevent errors from malformed data.

Secure Reconnection

When reconnecting, ensure you’re connecting to the legitimate endpoint. Validate SSL certificates.

Next Steps