Set Up Notifications with SignalR

Connect to the notification hub, recover missed inbox items, and handle origin metadata.

Set Up Notifications with SignalR

PixService notifications combine a durable inbox with live SignalR delivery. Use the inbox endpoints after login or reconnects, and keep the hub connected while the app is in the foreground.

Notifications published by API, administration, or background processing are persisted before live delivery is attempted. A retry can repeat the same notification ID. Upsert inbox entries by ID, or refresh the inbox when an event arrives, and preserve the server's read state. After reconnecting, refresh the inbox and visible resources: a successful live send does not guarantee that a browser received it. Current session, token and resource permissions are checked again before delivery.

Bootstrap

Call:

GET /v1/notifications/bootstrap

The response contains:

  • hubPath: the SignalR hub path, currently /sync-hub
  • unreadCount: the current unread count for the authenticated user

The count includes only unread notifications whose referenced resource is currently visible to the exact authenticated credential. A notification hidden by permission or entitlement loss does not leave an unread-count signal.

The server computes this count from the current authorized inbox, including for long histories. Clients can call bootstrap or unread-count without paging through old notifications first.

Matrix chat has a separate account summary at GET /v1/chat/summary when the chat catalog is enabled. Its unreadNotificationCount and highlightCount come from verified Matrix room sync and can be null while unavailable or stale. They do not represent this Pix inbox's unreadCount. Matrix message alerts continue through the Matrix HTTP pusher and APNs; a catalog change hint asks the client to refresh state and does not send another alert.

Connect to hubPath with the normal bearer token. Browser and mobile SignalR clients should pass the JWT as the access_token query-string token for WebSocket and SSE transports.

Inbox Endpoints

List notifications:

GET /v1/notifications?offset=0&limit=100

List only unread notifications:

GET /v1/notifications?unreadOnly=true

Get the unread count:

GET /v1/notifications/unread-count

Inbox pages skip hidden notifications before applying offset and limit, so a page does not have gaps caused by inaccessible resources. The count and page can change after a permission update; refresh both when access changes.

Mark one notification as read:

PATCH /v1/notifications/{notificationId}/read

Mark selected notifications as read:

POST /v1/notifications/read
{
  "notificationIds": [
    "22222222-2222-2222-2222-222222222222"
  ]
}

Mark all as read:

{
  "all": true
}

Hub Events

Subscribe to:

  • NotificationReceived
  • DocumentUploaded
  • StateUpdated
  • PositionChanged
  • SubscriptionRevoked

NotificationReceived sends one NotificationDto payload. Spatial sync events include the related location ID and the same notification payload.

Generic inbox delivery is authorized per connection, not from a broad user group. The server tracks the exact interactive session or personal access token actor, reloads live PAT identity and ceilings, and re-evaluates the notification origin immediately before sending. A narrowed PAT connection cannot receive a protected notification merely because another session for the same user can read it.

To receive location-scoped sync events, call:

JoinLocation(locationId)

Call LeaveLocation(locationId) when the view is closed.

JoinLocation evaluates the exact current credential, including live personal access token organization scopes and capability ceilings. Permission changes, entitlement transitions, and credential expiry can remove an active location subscription. In that case the hub emits:

SubscriptionRevoked("location", locationId)

Close or redact the related view and repeat its REST bootstrap before attempting another join. A previous group join is not durable authorization. Contextual chat is Matrix-backed and does not use this location-notification group for IntegratedChatUse membership.

Location-scoped events are re-authorized immediately before delivery. The server reloads the exact joined credential, including current personal access token identity and ceilings, and targets only connection IDs that still pass final location access. A stale group membership therefore cannot receive a new protected location event while asynchronous eviction is still converging.

The event type can narrow the audience further. DocumentUploaded requires current access to the referenced document before its ID, filename, or origin metadata is delivered. Annotation and position events require current LocationBundles=Read; a location-only viewer receives neither their names nor their counts or payload fields. Failing one of these payload checks does not remove a still-valid location subscription, so the connection can continue receiving permitted location-state events.

Origin Metadata

Notifications can include an origin object:

{
  "type": "spatial-document",
  "id": "22222222-2222-2222-2222-222222222222",
  "url": "/spatial/locations/33333333-3333-3333-3333-333333333333/documents/22222222-2222-2222-2222-222222222222",
  "organizationId": "11111111-1111-1111-1111-111111111111",
  "locationId": "33333333-3333-3333-3333-333333333333",
  "documentId": "22222222-2222-2222-2222-222222222222",
  "data": {
    "contentType": "application/pdf"
  }
}

Use origin.url as the preferred navigation target when present. Use the typed IDs as fallback routing inputs when the frontend has a different route structure. Treat every origin field as a navigation hint, not an access grant, and handle the destination returning inaccessible after a later permission change.

PixService filters resource-scoped notifications before recipient persistence, before inbox pagination and unread counting, and again before live delivery. Document origins require the matching Document read decision; Bundle and annotation origins require LocationBundles=Read; ordinary Location and Cluster origins require their final view capability. Organization notifications without a narrower resource require OrganizationView. If current access is lost, the complete notification and its label, URL, IDs, and metadata are omitted from subsequent lists and counts. Storage or Job overage alone does not hide data that remains readable.

Permission and tier-policy changes invalidate Notification and Directory projection generations through the durable permission outbox. Inbox recipient rows remain only candidate associations, and all inbox, count, read-state, and live-delivery paths re-evaluate current access instead of trusting a materialized recipient row. Directory results use the same live-query rule. Once the invalidation is durable, the server records terminal convergence for both surfaces. A hidden notification can therefore reappear after access is restored without having leaked its name, URL, IDs, metadata, or unread signal while access was absent.

On this page