Skip to content
FlightStack Docs
Sign in Start free

Realtime (SignalR) | FlightStack

FlightStack ships real-time updates over SignalR — pipeline run status, build log lines, agent heartbeats, deployment progress. The dashboard and the Flutter app both consume the same hubs.

{baseUrl}/hub/

For example: https://api.flightstack.voostack.com/hub/.

Pass your access token via accessTokenFactory (Dart / TS SignalR client) — SignalR sends it on the WebSocket handshake.

// Dart (signalr_netcore)
final connection = HubConnectionBuilder()
.withUrl(
'$baseUrl/hub',
HttpConnectionOptions(
accessTokenFactory: () async => await getAccessToken(),
),
)
.withAutomaticReconnect()
.build();
await connection.start();
// TypeScript (@microsoft/signalr)
const connection = new HubConnectionBuilder()
.withUrl(`${baseUrl}/hub`, { accessTokenFactory: getAccessToken })
.withAutomaticReconnect()
.build();
await connection.start();

Each connection inherits the user’s permissions — you only see events for resources you can already access through the REST API.

Once connected, subscribe to channels by calling the corresponding Subscribe* method on the hub. The server pushes events on the matching On* method.

MethodDirectionPurpose
SubscribeToRun(runId)C → SSubscribe to events for one run.
UnsubscribeFromRun(runId)C → SStop receiving events for that run.
OnRunStatusS → C{ runId, status, jobId?, jobStatus?, timestamp }
OnRunCompleteS → C{ runId, finalStatus, durationMs }
MethodDirectionPurpose
SubscribeToBuildLogs(buildId)C → SSubscribe to live log lines for a build.
UnsubscribeFromBuildLogs(buildId)C → SStop the stream.
OnLogEntryS → C{ buildId, step, level, message, timestamp }
MethodDirectionPurpose
SubscribeToAgents(orgId)C → SStream agent heartbeats and state changes for an org.
OnAgentHeartbeatS → C{ agentId, isOnline, currentJobId?, queueDepth }
OnAgentStateChangedS → C{ agentId, state } (online, offline, paused)
MethodDirectionPurpose
SubscribeToDeployment(deploymentId)C → SLive status for a deployment job.
OnDeploymentStatusS → C{ deploymentId, status, deployedUrl?, error? }

The official SignalR clients reconnect automatically with backoff (1s → 2s → 5s → 10s → 30s). After reconnect, re-subscribe to any channels you cared about — subscriptions are connection-scoped, not session-scoped.

connection.onreconnected(() => {
for (const runId of activeRuns) connection.invoke('SubscribeToRun', runId);
});

For high-volume log streams the server drops oldest entries on the client buffer if you fall more than 1,000 entries behind. If you need durable history, paginate GET /build/{id}/logs instead.