# Components

Here’s the whole system as a set of Lego bricks. You usually only touch one or two of them.

flowchart TB
  subgraph apps [Your applications]
    UI["Bell / inbox UI"]
    Server["Backend / jobs"]
  end

  subgraph npm [Published package]
    Client["@inovus-medical/notifications"]
    React["/react hooks"]
  end

  subgraph service [This repo]
    API["packages/api — Cloudflare Worker"]
    Core["packages/core — shared types"]
    Store["NotificationStore adapters"]
  end

  DB[(Supabase Postgres)]
  Cognito[AWS Cognito JWKS]

  UI --> Client
  Client --> React
  Client -->|HTTPS + JWT| API
  Server -->|HTTPS + M2M JWT| API
  Server --> Client
  API --> Core
  Client --> Core
  API --> Store
  Store --> DB
  API --> Cognito

# 1. The API (packages/api)

A small Cloudflare Worker that speaks HTTP.

  • Verifies Cognito (OIDC) JWTs
  • Enforces who can publish vs who can only read their inbox
  • Talks to the database through a store interface
  • Serves OpenAPI docs at /docs and /openapi.json
  • Also serves the legacy V1 /api/Notification/* routes for older callers

Production URL: https://notifications.totumcloud.com

You don’t have to deploy this yourself if you’re integrating with the shared Inovus instance — just point the client at that base URL.


# 2. The npm client (packages/client)

Package name: @inovus-medical/notifications

This is what most app developers live in day to day:

Export Purpose
NotificationsClient List, subscribe, mark read, publish, delete…
createNotificationService V1-shaped notification.success(title, body, url?) helpers
NotificationsApiError Typed errors with problem+json details
@inovus-medical/notifications/react useNotifications(client) hook

Zero runtime dependencies. React is an optional peer — only needed if you use the /react entry.


# 3. Shared types (packages/core)

Private package of Zod schemas + TypeScript types. It is the single source of truth for the wire format (camelCase JSON).

  • API validates requests with these schemas
  • OpenAPI is generated from them
  • Client types stay in lockstep (bundled into the published client)

You rarely import @inovus-medical/notifications-core directly — the client re-exports the types you need.


# 4. The database (Supabase / Postgres)

One notifications table. Important properties:

  • Keyed by the user’s Cognito sub (user_guid)
  • Race-safe unique index for dedupe: (user_guid, source, dedupe_key)
  • RLS enabled with no policies (deny-all) — only the Worker service role touches data
  • Retention cron purges old rows (default 90 days)

There is also an in-memory store for local dev and tests (wiped on restart).


# 5. Identity (Cognito)

We don’t invent accounts. We verify the JWTs you already issue:

  • User tokens → inbox ownership via sub
  • M2M (client credentials) tokens → publish via scope notifications/publish
  • Optional publisher groups for human/admin tokens

source on each notification is taken from the token (client_id or sub) — callers cannot spoof another producer.


# 6. What you still build

This service is deliberately headless:

You build We provide
Bell icon + dropdown items, unreadCount, mark/delete APIs
Toast banners — (out of scope)
Router navigation on click actionUrl field on each notification
Cognito login / token refresh getAccessToken callback you inject

# How the pieces show up in a typical app

// 1. Create the client once (token wiring is yours)
const client = new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com',
  getAccessToken: () => auth.getValidAccessToken(),
});

// 2. Optional: V1-shaped helpers for self-notifications
export const notification = createNotificationService(client);

// 3. Optional: React hook for the bell
const { notifications, unreadCount, markRead } = useNotifications(client);

Next: Why V2? if you’re migrating, or jump to Getting started.