Skip to main content

Overview

The Terminal Gateway uses API key-based authentication for both HTTP and WebSocket protocols. Each protocol has a different authentication mechanism:
ProtocolAuthentication Method
HTTP APIHMAC-SHA256 signature per request
WebSocket APIAPI key in headers or query parameters at connection time

API Key Provisioning

API keys are provisioned manually by Modulus Labs. To request API credentials:
1

Contact support

Email support@moduluslabs.io with your integration details.
2

Provide information

Include your organization name and intended use case (desktop POS, terminal integration, etc.).
3

Receive credentials

You will receive an API key and API secret pair. Store these securely.
Never expose your API key or secret in client-side code, version control, or logs. Store them in environment variables or a secure secrets manager.

HTTP API Authentication

HTTP requests require HMAC-SHA256 signature authentication. Each request must include three authentication headers.

Required Headers

HeaderDescription
x-api-keyYour API key
x-timestampISO 8601 timestamp (must be within 5 minutes of server time)
x-signatureBase64-encoded HMAC-SHA256 signature

String-to-Sign Format

Construct the string to sign using this format:
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + SHA256(BODY)
Where:
  • METHOD - HTTP method in uppercase (GET, POST)
  • PATH - Request path (e.g., /v1/terminals)
  • TIMESTAMP - Value of x-timestamp header
  • SHA256(BODY) - Hex-encoded SHA256 hash of request body (empty string hash for GET requests)

Signature Computation

signature = Base64(HMAC-SHA256(apiSecret, stringToSign))

Example

For a GET request to /v1/terminals at 2024-01-15T10:30:00.000Z:
StringToSign:
GET
/v1/terminals
2024-01-15T10:30:00.000Z
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Headers:
x-api-key: your-api-key
x-timestamp: 2024-01-15T10:30:00.000Z
x-signature: <base64-encoded-signature>
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 is the SHA256 hash of an empty string.

Code Examples

const crypto = require('crypto');

const API_KEY = process.env.MODULUS_API_KEY;
const API_SECRET = process.env.MODULUS_API_SECRET;
const BASE_URL = 'https://{your-api-endpoint}';

/**
 * Generate authentication headers for HTTP API requests
 * @param {string} method - HTTP method (GET, POST)
 * @param {string} path - Request path (e.g., /v1/terminals)
 * @param {object|null} body - Request body (null for GET requests)
 * @returns {object} Headers object with authentication
 */
function generateAuthHeaders(method, path, body = null) {
  const timestamp = new Date().toISOString();

  // SHA256 hash of body (empty string for GET)
  const bodyString = body ? JSON.stringify(body) : '';
  const bodyHash = crypto
    .createHash('sha256')
    .update(bodyString)
    .digest('hex');

  // Construct string to sign
  const stringToSign = `${method}\n${path}\n${timestamp}\n${bodyHash}`;

  // Compute HMAC-SHA256 signature
  const signature = crypto
    .createHmac('sha256', API_SECRET)
    .update(stringToSign)
    .digest('base64');

  return {
    'Content-Type': 'application/json',
    'x-api-key': API_KEY,
    'x-timestamp': timestamp,
    'x-signature': signature
  };
}

// Example: GET request
async function getTerminals() {
  const path = '/v1/terminals';
  const headers = generateAuthHeaders('GET', path);

  const response = await fetch(`${BASE_URL}${path}`, {
    method: 'GET',
    headers
  });

  return response.json();
}

// Example: POST request
async function createPayment(terminalId, paymentData) {
  const path = `/v1/terminals/${terminalId}/payments`;
  const headers = generateAuthHeaders('POST', path, paymentData);

  const response = await fetch(`${BASE_URL}${path}`, {
    method: 'POST',
    headers,
    body: JSON.stringify(paymentData)
  });

  return response.json();
}

Common Authentication Errors

Error CodeHTTP StatusDescriptionSolution
UNAUTHORIZED401Missing or invalid API keyVerify your API key is correct
INVALID_SIGNATURE401HMAC signature verification failedCheck signature computation
TIMESTAMP_EXPIRED401Request timestamp outside 5-minute windowEnsure system clock is synchronized

WebSocket API 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.

Code Examples

const WebSocket = require('ws');

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

// Option 1: Header-based authentication (recommended)
function connectWithHeaders() {
  const ws = new WebSocket(WS_ENDPOINT, {
    headers: {
      'x-api-key': API_KEY
    }
  });

  ws.on('open', () => {
    console.log('Connected with header authentication');
  });

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

  return ws;
}

// Option 2: Query parameter authentication
function connectWithQueryParam() {
  const ws = new WebSocket(`${WS_ENDPOINT}?x-api-key=${API_KEY}`);

  ws.on('open', () => {
    console.log('Connected with query parameter authentication');
  });

  return ws;
}

Security Best Practices

Protect Credentials

Store API keys and secrets in environment variables or a secure secrets manager. Never hardcode them in source code.

Use Headers for WebSocket

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

Synchronize Time

For HTTP API, ensure your system clock is synchronized with NTP. Timestamps must be within 5 minutes of server time.

Rotate Keys

Rotate API keys periodically and immediately if you suspect compromise. Contact support for key rotation.

Next Steps