# Getting started

Goal of this page: get a notification into an inbox and read it back with as little ceremony as possible.

You’ll need:

  • A Cognito access token for a signed-in user (you already have this in most Inovus apps)
  • Network access to the notifications service (https://notifications.totumcloud.com in production)
  • Node / browser environment that can call fetch

If the shared service isn’t deployed for your environment yet, ask your platform team — or follow Setup to stand one up.


# Step 1 — Install the client

npm install @inovus-medical/notifications
# or: pnpm add @inovus-medical/notifications

# Step 2 — Create a client once

Put this near your auth layer (one instance per app is enough):

import { NotificationsClient } from '@inovus-medical/notifications';

export const notificationsClient = new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com',
  // Return the current Cognito access token. Called per request,
  // and once more after a 401 so refreshed tokens are picked up.
  getAccessToken: () => auth.getValidAccessToken(),
});

Tip: Don’t put the token in localStorage yourself inside this package — just teach the client how to ask your existing auth module.


# Step 3 — Read the inbox

const { items, unreadCount, nextCursor } = await notificationsClient.list({
  limit: 20,
});

console.log(`${unreadCount} unread`);
for (const n of items) {
  console.log(n.severity, n.title, n.body);
}

Or keep the bell fresh without thinking about timers:

const stop = notificationsClient.subscribe(({ items, unreadCount }) => {
  renderBell(items, unreadCount); // your UI
});

// ALWAYS call this on logout / unmount
stop();

Default poll interval is 30 seconds. Unchanged polls return 304 and barely cost anything.


# Step 4 — React shortcut (optional)

import { useNotifications } from '@inovus-medical/notifications/react';
import { notificationsClient } from './notificationsClient';

export function NotificationBell() {
  const { notifications, unreadCount, loading, markRead, markAllRead, remove } =
    useNotifications(notificationsClient);

  if (loading) return <span>…</span>;

  return (
    <div>
      <span aria-label="Unread">{unreadCount}</span>
      <ul>
        {notifications.map((n) => (
          <li key={n.id}>
            <strong>{n.title}</strong>
            <p>{n.body}</p>
            <button type="button" onClick={() => markRead(n.id)}>
              Mark read
            </button>
            <button type="button" onClick={() => remove(n.id)}>
              Delete
            </button>
          </li>
        ))}
      </ul>
      <button type="button" onClick={() => markAllRead()}>
        Mark all read
      </button>
    </div>
  );
}

The hook owns subscribe/unsubscribe for you. You still own the markup and styling.


# Step 5 — Create a self-notification (browser)

Useful when the current user should get an inbox item from something that happened in the app:

import { createNotificationService } from '@inovus-medical/notifications';
import { notificationsClient } from './notificationsClient';

export const notification = createNotificationService(notificationsClient);

await notification.success(
  'Export ready',
  'Your CSV download is ready.',
  '/exports/latest',
);

This hits POST /v1/notifications/self. The recipient is always the signed-in user — they cannot target someone else.


# Step 6 — Publish from a backend (the common production path)

Background jobs usually notify another user. That needs a publisher token (M2M scope notifications/publish):

await notificationsClient.publish({
  userGuid: recipientCognitoSub,
  title: 'Review complete',
  body: 'Your assessment feedback is ready.',
  severity: 'success',
  actionUrl: `/video/${videoId}/feedback`,
  dedupeKey: `review_case:${caseId}`, // always set one for jobs
});

Full story: Publishing.


# Checklist: “Am I done?”

  • [ ] Client installed and constructed with baseUrl + getAccessToken
  • [ ] list or subscribe returns data for a real user token
  • [ ] Bell UI renders title / body / unreadCount (your components)
  • [ ] Logout calls stop() (or unmounts the React hook)
  • [ ] Producers set dedupeKey on every job publish

# Common first-day snags

Symptom Likely cause Fix
401 Unauthorized Missing/expired token Check getAccessToken; ensure Cognito refresh works
403 Forbidden on publish User token without publisher scope Use M2M token for cross-user publish; or use publishSelf / convenience API for self
Empty inbox Publishing to a different userGuid than the reader’s sub Recipients must match Cognito sub exactly
“Nothing updates live” Expecting WebSockets Polling is ~30s by default — see Advanced

More fixes: Troubleshooting.

Next: Setup if you need to deploy the service, or Everyday usage for the full inbox toolkit.