SignalR Connection
How to establish a real-time WebSocket connection to ZapTicket's SignalR hub for live messaging.
Overview
ZapTicket uses ASP.NET Core SignalR for real-time communication between the backend, agent dashboard, and visitor widget. The hub enables instant message delivery, typing indicators, presence updates, and conversation lifecycle events.
Two types of clients connect to the hub:
- Agents — authenticated via workspace JWT, join the tenant-wide group
- Visitors — authenticated via public site key, join their specific conversation group
Connection URL
wss://api.zapticket.app/hubs/chatAuthentication
Agents (JWT)
Agents authenticate by passing their workspace token as the access_token query parameter during the connection handshake:
import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr";
const connection = new HubConnectionBuilder()
.withUrl("https://api.zapticket.app/hubs/chat", {
accessTokenFactory: () => workspaceToken,
})
.withAutomaticReconnect()
.configureLogging(LogLevel.Information)
.build();
await connection.start();
console.log("Connected as agent");access_token query parameter on the WebSocket upgrade request. This is the standard SignalR auth pattern — do not pass it as a header.Visitors (Site Key)
Visitors authenticate with the public site key. The widget passes it as a site_key query parameter:
const connection = new HubConnectionBuilder()
.withUrl(`https://api.zapticket.app/hubs/chat?site_key=${siteKey}`, {
// No JWT — site key handles auth
})
.withAutomaticReconnect()
.build();
await connection.start();
console.log("Connected as visitor");Transport Negotiation
SignalR automatically negotiates the best transport in this order:
- WebSockets — preferred, full-duplex, lowest latency
- Server-Sent Events (SSE) — fallback for environments that block WebSockets
- Long Polling — last resort, works everywhere
In production, WebSockets are used 99% of the time. The fallbacks exist for corporate proxies and restrictive firewalls.
Auto-Reconnect
The .withAutomaticReconnect() configuration handles temporary disconnections (network hiccups, server restarts) automatically. The default retry intervals are:
Attempt 1: immediately (0ms)
Attempt 2: after 2 seconds
Attempt 3: after 10 seconds
Attempt 4: after 30 seconds
After 4 failures: stops reconnectingYou can customize the retry policy:
const connection = new HubConnectionBuilder()
.withUrl(hubUrl, { accessTokenFactory: () => token })
.withAutomaticReconnect([0, 1000, 5000, 10000, 30000, 60000])
.build();
connection.onreconnecting((error) => {
console.log("Reconnecting...", error);
// Show "reconnecting" UI state
});
connection.onreconnected((connectionId) => {
console.log("Reconnected:", connectionId);
// Re-join groups if needed
});
connection.onclose((error) => {
console.log("Connection closed permanently", error);
// Show "disconnected" UI state
});Connection Lifecycle
Disconnected → Connecting → Connected → Reconnecting → Connected
↓
Disconnected (if all retries fail)When an agent connects, the backend automatically adds them to the tenant:{tenantId} group. When a visitor connects, they're added to their conversation group upon starting or joining a conversation.