Messaging Events and Retrieving Notifications

Last updated: January 2026

This guide explains how to retrieve notifications from the Agglestone Messaging Service. You can retrieve individual notifications or paginated lists of notifications from a user’s inbox.

Setting Up the WebSocket Connection

Before you can receive notification events, you need to establish a WebSocket connection using SignalR:

// Install: npm install @microsoft/signalr@^10.0.0
import * as signalR from '@microsoft/signalr';

const tenantId = 'your-tenant-id'; // Get this from https://portal.agglestone.com
const apiBaseUrl = 'https://messaging.agglestone.com'; // Agglestone's messaging service URL

// Generate a unique client ID for this browser tab/window
const clientId = crypto.randomUUID();

// Create SignalR connection
const connection = new signalR.HubConnectionBuilder()
  .withUrl(`${apiBaseUrl}/tenant/${tenantId}/v1/hub?clientId=${encodeURIComponent(clientId)}`, {
    accessTokenFactory: () => accessToken // JWT token from authentication service
  })
  .withAutomaticReconnect()
  .build();

// Listen for notification events
connection.on("MessagingEvent", async (event) => {
  // IMPORTANT: The WebSocket event only contains lightweight information
  // You must fetch the full notification details using the REST API
  
  if (event.type === 'Message') {
    const notificationId = event.id;
    
    // Fetch the full notification details
    const notification = await getNotification(notificationId);
    
    // Display the notification in your UI
    displayNotification(notification);
  }
});

// Start the connection
try {
  await connection.start();
  console.log('Connected to SignalR hub');
} catch (error) {
  console.error('Connection error:', error);
}

Retrieving Individual Notifications

When you receive a WebSocket MessagingEvent for a notification, you need to fetch the full notification details from the REST API. The WebSocket event only contains lightweight information (notification ID, timestamp), so you must make an API call to get the complete notification data.

When to Retrieve Individual Notifications

You should retrieve an individual notification when:

  • You receive a MessagingEvent through the WebSocket connection
  • You need to display a specific notification that was referenced by ID
  • You want to refresh or verify notification details

Retrieving a Notification by ID

Endpoint: GET /tenant/{tenantId}/v1/Notifications/{notificationId}

Response:

{
  "id": "notification-id-456",
  "tenantId": "tenant-id-123",
  "userId": "user-id-123",
  "type": "SystemAnnouncement",
  "subject": "System Maintenance Scheduled",
  "message": "We will be performing scheduled maintenance on Saturday at 2 AM.",
  "htmlContent": "<p>We will be performing <strong>scheduled maintenance</strong> on Saturday at 2 AM.</p>",
  "summary": "Maintenance scheduled for Saturday",
  "priority": "High",
  "status": "Sent",
  "createdAt": "2024-01-15T10:30:00Z",
  "readAt": null,
  "metadata": {
    "actionUrl": "/maintenance"
  }
}

Example: Retrieving an Individual Notification

The getNotification function is called from the MessagingEvent handler shown in the connection setup above:

async function getNotification(notificationId: string) {
  const response = await fetch(
    `${apiBaseUrl}/tenant/${tenantId}/v1/Notifications/${notificationId}`,
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      }
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to fetch notification: ${response.statusText}`);
  }

  return await response.json();
}

Retrieving Paginated Notification Lists

When you need to load notification history, display an inbox interface, or implement infinite scroll, you should retrieve a paginated list of notifications.

When to Retrieve Paginated Notification Lists

You should retrieve paginated notification lists when:

  • Loading inbox: When a user opens their inbox, you need to load recent notifications to display
  • Implementing infinite scroll: As users scroll down in their inbox, you load older notifications
  • Catching up after reconnection: When a user reconnects after being offline, you fetch notifications they missed
  • Initial inbox load: When displaying the inbox for the first time, you load the most recent notifications

Note: It’s perfectly fine to retrieve notifications that you may have already loaded. You can avoid showing duplicates on the screen by checking the id field of each notification and only displaying notifications that haven’t been shown before. This is especially useful when loading older notifications for infinite scroll or when catching up after reconnection, as there may be overlap between the notifications you’ve already displayed and the new notifications you’re fetching.

Retrieving Notifications with Pagination

Endpoint: GET /tenant/{tenantId}/v1/Notifications

Query Parameters:

  • pageNumber (optional, default: 1): The page number to retrieve
  • pageSize (optional, default: 10, max: 50): The number of notifications per page
  • createdAfter (optional): ISO 8601 date/time – only return notifications created after this date

Response:

{
  "data": [
    {
      "id": "notification-id-456",
      "tenantId": "tenant-id-123",
      "userId": "user-id-123",
      "type": "SystemAnnouncement",
      "subject": "System Maintenance Scheduled",
      "message": "We will be performing scheduled maintenance on Saturday at 2 AM.",
      "priority": "High",
      "status": "Sent",
      "createdAt": "2024-01-15T10:30:00Z",
      "readAt": null
    },
    {
      "id": "notification-id-455",
      "tenantId": "tenant-id-123",
      "userId": "user-id-123",
      "type": "UserInvite",
      "subject": "You've been invited",
      "message": "You've been invited to join the team.",
      "priority": "Normal",
      "status": "Read",
      "createdAt": "2024-01-15T09:00:00Z",
      "readAt": "2024-01-15T09:05:00Z"
    }
  ],
  "pageNumber": 1,
  "pageSize": 10,
  "totalCount": 25,
  "totalPages": 3,
  "hasPreviousPage": false,
  "hasNextPage": true
}

The response includes:

  • data: Array of notification objects
  • pageNumber: Current page number
  • pageSize: Number of items per page
  • totalCount: Total number of notifications in the inbox
  • totalPages: Total number of pages
  • hasPreviousPage: Whether there are older notifications
  • hasNextPage: Whether there are newer notifications

Example: Retrieving Paginated Notifications

async function getNotifications(
  pageNumber: number = 1,
  pageSize: number = 20,
  createdAfter?: Date
) {
  const params = new URLSearchParams({
    pageNumber: pageNumber.toString(),
    pageSize: pageSize.toString()
  });

  if (createdAfter) {
    params.append('createdAfter', createdAfter.toISOString());
  }

  const response = await fetch(
    `${apiBaseUrl}/tenant/${tenantId}/v1/Notifications?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      }
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to fetch notifications: ${response.statusText}`);
  }

  return await response.json();
}

// Load recent notifications when opening the inbox
async function loadInbox(pageNumber: number = 1) {
  const result = await getNotifications(pageNumber, 20);
  
  // Display notifications in your UI
  result.data.forEach(notification => {
    displayNotification(notification);
  });
  
  return result;
}

// Load older notifications for infinite scroll
async function loadOlderNotifications(currentPage: number) {
  const result = await getNotifications(currentPage + 1, 20);
  
  // Append older notifications to your inbox UI
  result.data.forEach(notification => {
    appendNotification(notification);
  });
  
  return result;
}

// Load notifications created after a specific date
async function loadNotificationsSince(date: Date) {
  const result = await getNotifications(1, 50, date);
  
  // Display notifications created since the specified date
  result.data.forEach(notification => {
    displayNotification(notification);
  });
  
  return result;
}

Best Practices

Loading Inbox

When a user opens their inbox, load the most recent notifications first:

// Load the most recent 20 notifications when opening the inbox
const notifications = await getNotifications(1, 20);
// Notifications are returned in reverse chronological order (newest first)
notifications.data.forEach(notification => {
  displayNotification(notification);
});

Implementing Infinite Scroll

For infinite scroll, load older notifications as the user scrolls down:

let currentPage = 1;
let hasMoreNotifications = true;

async function loadMoreNotifications() {
  if (!hasMoreNotifications) return;
  
  const result = await getNotifications(currentPage + 1, 20);
  
  if (result.data.length > 0) {
    // Append older notifications
    result.data.forEach(notification => {
      appendNotification(notification);
    });
    currentPage++;
    hasMoreNotifications = result.hasNextPage;
  } else {
    hasMoreNotifications = false;
  }
}

Catching Up After Reconnection

When a user reconnects after being offline, fetch notifications they missed:

// Store the timestamp of the last notification you received
let lastNotificationTimestamp: Date | null = null;

// When reconnecting, fetch notifications since the last notification
async function catchUpAfterReconnect() {
  if (lastNotificationTimestamp) {
    const missedNotifications = await getNotifications(
      1,
      50,
      lastNotificationTimestamp
    );
    
    // Display missed notifications
    missedNotifications.data.forEach(notification => {
      displayNotification(notification);
      lastNotificationTimestamp = new Date(notification.createdAt);
    });
  }
}

Error Handling

Common errors you may encounter:

  • 401 Unauthorized: Your access token is missing, invalid, or expired. Refresh your token and retry.
  • 403 Forbidden: The tenant ID doesn’t match your authentication, or you’re trying to access another user’s notifications.
  • 404 Not Found: The notification doesn’t exist, or you don’t have access to it.
  • 400 Bad Request: Invalid query parameters (e.g., invalid date format, page size too large).

Always check the response status and handle errors appropriately in your application.

Ready to manage notifications? Check out Managing Notifications.