GNSDeveloper Docsv1.0 (Current)
Channels

In-App Notification Center

Real-time user notification inbox with Server-Sent Events (SSE) live updates, badge counts, and read/unread status tracking.

In-App Notification Center

Deliver rich notifications directly into your web or mobile user interface with real-time Server-Sent Events (SSE) and synchronized read states.


1. Sending an In-App Notification

To dispatch a message into a user's notification bell/drawer:

curl -X POST "https://api.gns.iitdeveloper.com/api/v1/notifications" \
-H "Authorization: Bearer gns_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
  "channel": "in_app",
  "recipient": "usr_99812",
  "title": "Team Invitation",
  "body": "Sarah invited you to collaborate on the Q4 roadmap.",
  "data": {
    "action_url": "/projects/q4-roadmap",
    "avatar_url": "https://cdn.acme.com/avatars/sarah.jpg",
    "category": "invitations"
  }
}'

2. Fetching User Inbox (GET /api/v1/inbox)

Frontend clients query notifications for the currently logged-in user:

GET /api/v1/inbox?unread_only=false&limit=20
Authorization: Bearer <user_session_token>
Response200 OK
{
  "unread_count": 1,
  "notifications": [
    {
      "id": "inapp_01J7K3X9AB0C",
      "title": "Team Invitation",
      "body": "Sarah invited you to collaborate on the Q4 roadmap.",
      "is_read": false,
      "created_at": "2026-09-13T12:50:00Z",
      "data": {
        "action_url": "/projects/q4-roadmap"
      }
    }
  ]
}

3. Real-Time SSE Event Stream (GET /api/v1/in-app/stream)

Instead of polling, client applications subscribe to real-time events over a persistent HTTPS Server-Sent Events (SSE) connection.

Connection & Headers

  • URL: GET https://api.gns.iitdeveloper.com/api/v1/in-app/stream
  • Media Type: text/event-stream
  • Headers:
    • Authorization: Bearer <user_token> (Required — In-app user JWT bearer token with tenant and application claims)
    • Accept: text/event-stream (Required)
    • Last-Event-ID: <id> (Optional — Replays missed notifications created after this event ID upon reconnection)

Note on Browser Authentication: Standard browser EventSource does not support custom HTTP request headers like Authorization: Bearer <token>. Because GNS authenticates users via the Authorization header, client applications should connect using a fetch()-based readable stream reader or @microsoft/fetch-event-source.

Event Types Emitted

  1. connection.ready: Emitted immediately upon connection with { "connection_id": "con_..." }.
  2. notification.created: Broadcast when a new in-app notification is delivered to the user.
  3. notification.updated: Broadcast when a notification is marked as read or updated.
  4. heartbeat: Periodic keep-alive ping emitted every 15 seconds to prevent network timeouts.

Client Implementation Examples

// Real-time In-App SSE Stream Client using fetch()
// Allows passing the required Authorization: Bearer header
async function subscribeToInAppStream({
userToken,
lastEventId,
onNotification,
onUpdate,
onError,
}: {
userToken: string;
lastEventId?: string;
onNotification: (notification: Record<string, unknown>) => void;
onUpdate: (updated: Record<string, unknown>) => void;
onError?: (err: unknown) => void;
}) {
const headers: Record<string, string> = {
  Authorization: `Bearer ${userToken}`,
  Accept: 'text/event-stream',
};

if (lastEventId) {
  headers['Last-Event-ID'] = lastEventId;
}

try {
  const response = await fetch('https://api.gns.iitdeveloper.com/api/v1/in-app/stream', {
    headers,
  });

  if (!response.ok) {
    throw new Error(`SSE stream connection failed: ${response.status} ${response.statusText}`);
  }

  const reader = response.body?.getReader();
  if (!reader) throw new Error('ReadableStream not supported');

  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const blocks = buffer.split('\n\n');
    buffer = blocks.pop() || '';

    for (const block of blocks) {
      let eventType = 'message';
      let eventData = '';
      let eventId = '';

      for (const line of block.split('\n')) {
        if (line.startsWith('event: ')) {
          eventType = line.slice(7).trim();
        } else if (line.startsWith('data: ')) {
          eventData = line.slice(6).trim();
        } else if (line.startsWith('id: ')) {
          eventId = line.slice(4).trim();
        }
      }

      if (eventId) lastEventId = eventId;

      if (eventType === 'connection.ready') {
        console.log('Connected to In-App Stream:', JSON.parse(eventData));
      } else if (eventType === 'notification.created') {
        onNotification(JSON.parse(eventData));
      } else if (eventType === 'notification.updated') {
        onUpdate(JSON.parse(eventData));
      }
    }
  }
} catch (err) {
  onError?.(err);
}
}

On this page