MODULES / ENGAGEMENT

Notifications

Free · MITv0.1.3

In-app inbox with a live bell, per-category email and in-app preferences, and email delivery through Resend with a delivery log.

terminalnpx ajolla add notifications

What you get

  • In-app inbox with unread-first ordering, mark one or all read, and pagination
  • NotificationBell header component with a live unread count over Supabase Realtime
  • Per-category in-app and email preferences plus a global email opt-out
  • Email via Resend with idempotency keys, or a console log provider in development
  • Accessible HTML and plain-text emails with escaped content and safe links
  • Delivers notifications.requested from any module, idempotent and retried on provider outages
  • Admin delivery log with status filter and a send-a-test button

Routes it adds

/account/notifications/admin/notifications/notifications

Plus 1 Supabase migration with row-level security, and an AGENTS.md your coding agent reads after install.

Installs with

Kernel, Profiles (added automatically).

Configuration

RESEND_API_KEY
Resend API key. When unset, emails are printed to the server console instead of sent.
NOTIFICATIONS_FROM_EMAIL
Verified sender, e.g. "Acme <notifications@acme.com>". Required when RESEND_API_KEY is set.
NOTIFICATIONS_APP_NAME
App name shown in notification emails (default "Notifications").
NEXT_PUBLIC_SITE_URL
Your site's origin (https://example.com), used for absolute links in emails.
NOTIFICATIONS_LOGO_URL
https URL of a PNG/JPG logo shown at the top of every email.
NOTIFICATIONS_BRAND_COLOR
Hex colour for email buttons and the accent bar, e.g. #4f46e5 (default #131316).

Events

Other modules (and your own code) can react to these:

notifications.deliverednotifications.failed

Use it from any stack

Ajolla Hosted runs the same code as a multi-tenant API. Create an app in your dashboard, enable Notifications, and call /api/m/notifications/v1 with your app key.

OpenAPI document — feed it to Ajolla to generate SDKs in 7 languages and an MCP server.

AGENTS.md

Read the instructions installed with this module
markdown# Ajolla Notifications

In-app inbox, per-user preferences and email delivery. Any module (or your own code) sends a notification by emitting `notifications.requested`; this module delivers it according to the recipient's preferences.

## Installed
- **Routes**
  - `/notifications` — inbox: unread first, open (marks read and follows the link), mark one or all read, pagination.
  - `/account/notifications` — preferences: in-app and email per category, plus "no notification emails".
  - `/admin/notifications` — delivery log with a status filter, and "Send test notification to me" (permission `notifications.manage`).
- **Components** — `components/ajolla/notifications/notification-bell.tsx`: `<NotificationBell />` (Server Component) shows the unread count and links to `/notifications`; it updates live over Supabase Realtime and re-counts when the tab regains focus.
- **Library** — `lib/ajolla/notifications/`
  - `core.ts` — `notify(scope, input, deps)`, `listNotifications`, `countUnread`, `markRead`, `getPreferences`, `setPreferences`, `listDeliveries`, zod schemas and pure helpers (`planChannels`, `isChannelEnabled`, `isSafeNotificationUrl`).
  - `email.ts` — `EmailProvider` interface, `createResendProvider` (fetch, `Idempotency-Key`), `createLogProvider`, `renderNotificationEmail`.
  - `config.ts` — builds the provider from env. `categories.ts` — the categories (and labels) your app sends.
- **Subscriber** — `lib/ajolla/subscribers/notifications.ts` consumes `notifications.requested`.
- **Tables** — `ajolla_notifications_notifications` (inbox, in the `supabase_realtime` publication), `ajolla_notifications_preferences`, `ajolla_notifications_user_settings`, `ajolla_notifications_deliveries` (email log).
- **Events** — emits `notifications.delivered` and `notifications.failed` (email), consumes `notifications.requested`.
- **Env** — `RESEND_API_KEY`, `NOTIFICATIONS_FROM_EMAIL` (required with the key), `NOTIFICATIONS_APP_NAME`, `NEXT_PUBLIC_SITE_URL`.

## Setup
1. Apply migrations: `supabase db push`.
2. Email: create a Resend API key, verify your sending domain, then set `RESEND_API_KEY` and `NOTIFICATIONS_FROM_EMAIL`. Without a key, emails are printed to the server console (marked DEVELOPMENT EMAIL).
3. Set `NEXT_PUBLIC_SITE_URL` so links in emails are absolute.
4. Put `<NotificationBell />` in your header (e.g. `app/layout.tsx`).
5. Make sure the kernel's `/api/ajolla/events` cron is scheduled: failed email attempts are retried from there.
6. Open `/admin/notifications` as an admin and send yourself a test.

## Sending notifications
```ts
import { emitEvent } from "@/lib/ajolla/events";
await emitEvent("notifications.requested", {
  userId: user.id,            // or email: "someone@example.com"
  category: "billing",        // lowercase key; users can turn categories off
  title: "Your invoice is ready",
  body: "Invoice #1042 for $49.00 is available.",
  url: "/billing",            // a path or an https:// URL
  channels: ["in_app", "email"], // optional; default is every channel the user allows
});
```
Delivery is idempotent per event id: redelivered events never duplicate inbox rows or emails (the event id is also Resend's idempotency key). Provider outages (429/5xx/network) throw so the kernel retries with backoff; permanent rejections (other 4xx) are recorded as `failed`. To send directly from server code, call `notify()` with a secret-key scope (`selfHostedScope(createAjollaAdminClient())`); signed-in users cannot write inbox or log rows.

## Security
- Users read only their own notifications and may change only `read_at` (column privilege + RLS). They manage only their own preferences.
- The delivery log is readable only with `notifications.manage`. Anonymous visitors see nothing.
- Links are restricted to same-site paths and https URLs, both in the core and by a database check.

## Customise

- **Branding:** set `NOTIFICATIONS_LOGO_URL` (an https PNG/JPG; SVG doesn't render in most mail apps) and `NOTIFICATIONS_BRAND_COLOR` (hex) to put your logo above every email and colour its button and accent bar. Without a logo the header shows `NOTIFICATIONS_APP_NAME`.
- Add your categories, labels and descriptions to `lib/ajolla/notifications/categories.ts`.
- Restyle via the kit's `--aj-*` variables, or edit `notifications.module.css` / `notification-bell.module.css`.
- Use another email provider by implementing `EmailProvider` in `email.ts` and returning it from `config.ts`.