This documentation describes the WebSocket/PubSub solution specifically for enterprise clients. Enterprise deployments use a different authentication flow than our public plans to provide enhanced security and reliability.
The authentication process involves two steps:
Using the credentials provided to your team, make a request to Microsoft's OAuth endpoint:
curl -X POST "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id={clientId}&scope={scope}&client_secret={clientSecret}&grant_type=client_credentials"
Upon success, you will receive a response like:
{
"token_type": "Bearer",
"expires_in": 3599,
"ext_expires_in": 3599,
"access_token": "eyJ0eXAiOi...JWT_TOKEN_HERE"
}
Using the access_token
from the previous step, authenticate with our server:
curl -X POST "https://{ourServerName}/.auth/login/aad" \
-H "Content-Type: application/json" \
-d '{"access_token": "eyJ0eXAiOi...JWT_TOKEN_HERE"}'
If successful, you'll receive a response containing your authentication token:
{
"authenticationToken": "eyad1ad...another_token_here",
"user": {
"userId": "sid:a50f42b1481ca31efcf853fabb13e95b"
}
}
The authenticationToken
expires after one month. We recommend:
To use the token with our WebSocket service:
authenticationToken
in your WebSocket connection headers as X-ZUMO-AUTH
// Example WebSocket connection with authentication
const socket = new WebSocket('wss://{ourServerName}/ws');
// Set the authentication header before connecting
socket.setRequestHeader('X-ZUMO-AUTH', authenticationToken);
After successfully connecting to the WebSocket server, you must send an email registration message:
// Send email registration after connection is established
socket.onopen = function() {
// Register email for support purposes
socket.send(JSON.stringify({
"email": "[email protected]"
}));
};
To prevent connection timeouts and detect disconnections early, implement a ping-pong mechanism:
Enterprise WebSocket responses use a more compact format to optimize bandwidth and reduce latency. The field names are shortened compared to our public solutions.
Real-time market quotes use the following abbreviated field names:
Standard Field | Enterprise Format | Description |
---|---|---|
code | c | Symbol identifier |
lp_time | lt | Last price timestamp |
volume | v | Trading volume |
last_price | lp | Last trade price |
ask | a | Ask price |
bid | b | Bid price |
ask_size | as | Size of ask orders |
bid_size | bs | Size of bid orders |
Example Quote Response:
{
"c": "NASDAQ:AAPL",
"lt": 1733432340,
"v": 533779,
"lp": 243.08,
"a": 243.09,
"b": 243.08,
"as": 520.0,
"bs": 430.0
}
OHLCV bar data uses these abbreviated field names:
Standard Field | Enterprise Format | Description |
---|---|---|
code | c | Symbol identifier |
bar_end | be | Bar end timestamp |
last_update | lu | Last update timestamp |
bar_type | bt | Bar interval type |
series | s | Array of OHCLV data |
Within each series bar object:
Standard Field | Enterprise Format | Description |
---|---|---|
time | t | Bar timestamp |
open | o | Opening price |
high | h | Highest price |
low | l | Lowest price |
close | c | Closing price |
volume | v | Trading volume |
Example Series Response:
{
"c": "NASDAQ:AAPL",
"be": 1733432399,
"lu": 1733432399820,
"bt": "1m",
"s": [
{
"t": 1733432340,
"o": 242.89,
"h": 243.09,
"l": 242.82,
"c": 243.08,
"v": 533779
}
]
}
Below are examples demonstrating how to authenticate and connect to our enterprise WebSocket service.
import axios from 'axios';
import WebSocket from 'ws';
// Your enterprise credentials (provided by our team)
const tenantId = 'your-tenant-id';
const clientId = 'your-client-id';
const clientSecret = 'your-client-secret';
const scope = 'https://yourscope/.default';
const hostName = 'your-enterprise-server.insightsentry.com';
async function main() {
try {
// Step 1: Obtain Microsoft Azure access token
const tokenResponse = await axios.post(
`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
new URLSearchParams({
'client_id': clientId,
'scope': scope,
'client_secret': clientSecret,
'grant_type': 'client_credentials'
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
const accessToken = tokenResponse.data.access_token;
// Step 2: Authorize with our app to get authentication token
const authResponse = await axios.post(
`https://${hostName}/.auth/login/aad`,
{ 'access_token': accessToken },
{
headers: {
'Content-Type': 'application/json'
}
}
);
const authToken = authResponse.data.authenticationToken;
// Step 3: Connect to WebSocket with the authentication token
connectToWebSocket(authToken);
} catch (error) {
console.error('Authentication error:', error);
}
}
function connectToWebSocket(authToken: string) {
// Create WebSocket connection
const ws = new WebSocket(`wss://${hostName}/ws`, {
headers: {
'X-ZUMO-AUTH': authToken
}
});
let pingInterval: NodeJS.Timeout;
ws.on('open', () => {
console.log('WebSocket connection established');
// Send initialization message
ws.send(JSON.stringify({ 'email': '[email protected]' }));
// Set up ping interval (every 15 seconds)
pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping');
console.log('Ping sent');
}
}, 15000);
});
ws.on('message', (data) => {
// Convert buffer to string if needed
const message = data.toString();
// Ignore pong responses
if (message === 'pong') {
return;
}
// Process actual data messages
console.log('Received:', message);
try {
const jsonData = JSON.parse(message);
// Process your JSON data here
} catch (e) {
// Handle non-JSON messages if needed
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
ws.on('close', () => {
console.log('WebSocket connection closed');
// Clear ping interval
clearInterval(pingInterval);
// Optional: Implement reconnection logic here
});
}
main();