Connection Management
Notiway is built on SignalR and inherits its connection model. Any standard SignalR client library works with Notiway out of the box.
Connecting
Clients connect to the gateway over HTTP. Before upgrading the connection, Notiway authenticates the request using the configured Auth plugin. If authentication passes, the connection is upgraded to a persistent WebSocket which is a long-lived bidirectional tunnel that stays open until the client disconnects.
On connect, Notiway automatically replays any persisted notifications addressed to that user or the global audience, so clients never miss events they received while offline.
Reconnection
If the connection is interrupted, SignalR client libraries can be configured to reconnect automatically. Notiway treats a reconnect as a fresh connection. Persisted notifications are replayed again on reconnect, ensuring no events are missed.
Enable automatic reconnect in your client:
const connection = new signalR.HubConnectionBuilder()
.withUrl("https://your-gateway/notifications")
.withAutomaticReconnect()
.build();var connection = new HubConnectionBuilder()
.WithUrl("https://your-gateway/notifications")
.WithAutomaticReconnect()
.Build();final connection = HubConnectionBuilder()
.withUrl("https://your-gateway/notifications")
.withAutomaticReconnect()
.build();let connection = HubConnectionBuilder()
.withUrl(url: "https://your-gateway/notifications")
.withAutoReconnect()
.build()val connection = HubConnectionBuilder.create("https://your-gateway/notifications")
.withAutoReconnect()
.build()Authentication
Every incoming connection is authenticated using the configured Auth plugin before the WebSocket upgrade is allowed. Notiway supports any authentication method — JWT, OAuth, custom token validation, or no authentication for development.
Only one Auth plugin can be active at a time. See Auth Plugins for all available options and their configuration.
Tenant Validation
When multitenancy is enabled, Notiway validates tenant membership using the configured Tenant Validation plugin. This ensures a client can only join audiences within tenants they belong to.
Only one Tenant Validation plugin can be active at a time. See Tenant Validation Plugins for all available options.
Joining Audiences
On connect, the client automatically joins the Global, User, and Connection audiences. To receive notifications from a Tenant or Group audience, the client must explicitly join it by invoking Subscribe.
The client sends a Subscribe hub invocation with a SubscriptionDto:
| Field | Type | Required | Description |
|---|---|---|---|
audienceType | number | Yes | 2 for Tenant, 3 for Group |
audienceValue | string | Yes* | Tenant ID or group name |
tenantId | string | No | Required when joining a tenant-scoped group |
Join examples:
// Join a tenant audience
await connection.invoke("Subscribe", {
audienceType: 2,
audienceValue: "acme-corp"
});// Join a named group across all tenants
await connection.invoke("Subscribe", {
audienceType: 3,
audienceValue: "admins"
});// Join a named group within a specific tenant
// Requires joining the tenant audience first
await connection.invoke("Subscribe", {
audienceType: 3,
audienceValue: "admins",
tenantId: "acme-corp"
});On successfully joining an audience, Notiway automatically replays any persisted notifications for that audience.
Order matters: Register your notification type handlers before joining an audience. Persisted notifications are replayed at the moment of joining, so any handlers that are not yet registered will miss them.
Note: You cannot join User or Connection audiences directly — the gateway manages those automatically based on the authenticated user identity and connection ID.
Leaving Audiences
To leave an audience, invoke Unsubscribe with the same SubscriptionDto used to join. Once unsubscribed, the client will no longer receive notifications routed to that audience.
Leave examples:
// Leave a tenant audience
await connection.invoke("Unsubscribe", {
audienceType: 2,
audienceValue: "acme-corp"
});// Leave a named group across all tenants
await connection.invoke("Unsubscribe", {
audienceType: 3,
audienceValue: "admins"
});// Leave a named group within a specific tenant
await connection.invoke("Unsubscribe", {
audienceType: 3,
audienceValue: "admins",
tenantId: "acme-corp"
});Note: Global, User, and Connection audiences are managed automatically by the gateway and cannot be unsubscribed from manually.
Persisted Notification Actions
For persisted notifications, clients can update their read and deleted state via hub methods. These state changes are synced across all of the user’s active connections.
Mark as read:
await connection.invoke("MarkAsRead", audienceId, eventId);
// e.g. MarkAsRead("tenant:acme-corp", "order-service-order-placed-2025-01-01T10:00:00Z")
Mark as unread:
await connection.invoke("MarkAsRead", audienceId, eventId, false);Mark as deleted:
await connection.invoke("MarkAsDeleted", audienceId, eventId);Restore (undelete):
await connection.invoke("MarkAsDeleted", audienceId, eventId, false);Both methods accept an optional boolean parameter (isRead / isDeleted) that defaults to true. Pass false to reverse the action.
When a status changes, Notiway notifies all connected clients of that user with an EventReadStatusChange or EventDeletedStatusChange event so all devices stay in sync.
Disconnection
When a client disconnects — whether intentionally or due to a network interruption — Notiway removes it from all audience groups and cleans up its entry in the connection cache. No further notifications are delivered to that connection.
If the client reconnects, the full connection lifecycle starts again from the beginning.