User Notifications
Reconnect an authenticated WebSocket and seed balances, positions, and orders from REST
Authenticate the socket to receive updates for the signed-in user. Message shapes are in WebSocket Protocol.
No initial state is sent when the socket authenticates. Seed balances, positions, and orders from GET /execution/balances, GET /execution/positions, and GET /execution/orders, then apply socket updates. See Portfolio and Order lifecycle.
A fill is the socket event order_event with event filled or partial_fill, and the webhook event trades.filled. See Webhooks.
Refresh-aware reconnect
The socket's user session lasts as long as the access token. The gateway sends auth_expiring
about a minute before expiry, and auth_expired when the session ends. Refresh the token and
send authenticate again. When you reconnect, pass the last eventId you processed as
resumeFrom, and events sent while you were away are replayed. Seed from REST only when the
resumed frame says complete: false.
let accessToken = initialAccessToken;
let lastEventId: string | undefined;
const seen = new Set<string>();
function connect(attempt = 0) {
const ws = new WebSocket(`wss://ws.t.sufwankhalid.com/ws?appId=${appId}`);
const authenticate = () =>
ws.send(JSON.stringify({ action: "authenticate", token: accessToken, resumeFrom: lastEventId }));
ws.onmessage = async (frame) => {
const msg = JSON.parse(frame.data);
switch (msg.type) {
case "connected":
attempt = 0;
authenticate();
return;
case "auth_expiring":
case "auth_expired":
try {
accessToken = await client.refreshAccessToken();
authenticate();
} catch {
await client.signOut();
ws.close();
}
return;
case "resumed":
if (!msg.complete) await seedFromRest(); // some events may be missing
return;
}
if (msg.eventId) {
if (seen.has(msg.eventId)) return; // a resume can resend events you have
seen.add(msg.eventId);
lastEventId = msg.eventId;
}
applyUserEvent(msg);
};
ws.onclose = (event) => {
if (event.code === 4001 || event.code === 4003) return; // configuration error
const delay = Math.min(1000 * 2 ** attempt, 30_000) * (0.5 + Math.random() / 2);
setTimeout(() => connect(attempt + 1), delay);
};
}
await seedFromRest();
connect();The SDK does all of this for you: client.createWebSocket() authenticates by message, refreshes
on auth_expiring, resumes from the last eventId, de-duplicates, and reports onResumed.
Server-side: every user's events
A backend can receive all users' events for the app on one API-key connection instead of one socket per user. See App events.