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>{
"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
EventSourcedoes not support custom HTTP request headers likeAuthorization: Bearer <token>. Because GNS authenticates users via theAuthorizationheader, client applications should connect using afetch()-based readable stream reader or@microsoft/fetch-event-source.
Event Types Emitted
connection.ready: Emitted immediately upon connection with{ "connection_id": "con_..." }.notification.created: Broadcast when a new in-app notification is delivered to the user.notification.updated: Broadcast when a notification is marked as read or updated.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);
}
}