All docs

Notifications

Notifications

Linear-style notification system shipped in task 45.

Channels

  • in_app — feed reachable via /api/notifications (GET) + sidebar bell

(rendered by the app shell layout).

  • email — sent via the shared nodemailer transport (Hostinger SMTP in

prod, Mailpit in dev). Plain text + minimal HTML. MJML templates are a follow-up.

  • slack, discord — reserved values in the enum so the schema is

forward-compatible; not wired.

Defaults per kind

Critical events (task.needs_review, task.verification_failed, plan.proposed, quota.*, billing.*, security.new_login, member.invited, agent.token_revoked) default to in-app and email at instant frequency.

Chatty events (task.completed, plan.approved, plan.rejected, knowledge.superseded, member.joined) default to in-app only.

Users can override per (kind, channel) in notification_preferences.

Hardcoded overrides

quota.exceeded + security.new_login always email — even if the user opts out — because they protect the user's own budget / account.

Frequency batching

notification_preferences.frequency accepts instant | hourly | daily. v1 dispatch is always instant; hourly + daily are recognized at write time but ignored at dispatch. A digest worker can be added later without a schema change.

Service

notify(db, input, getEmail?) is the single dispatch entry point. Callers (route actions, MCP tools, admin service) pass the event + recipient IDs + an optional getEmail resolver. The function loads preferences in one query, writes the in-app row, and synchronously sends the email.

When the BullMQ worker arrives, swap the sync send for an enqueue — the signature stays the same.

Schema

packages/db/migrations/0017_notifications.sql adds two tables:

  • notifications — append-only feed; soft-archive via archived_at.
  • notification_preferences — per-(user, org, kind, channel) toggle +

frequency.

API

  • GET /api/notifications → JSON list of unread for the signed-in user.
  • POST /api/notifications with op=mark-read&ids=a,b,c → mark

notifications as read.

Out of scope for v1

  • BullMQ email worker — sync send for now.
  • MJML templates — plain HTML for now.
  • Slack / Discord webhooks.
  • Realtime SSE bridge for notification feed — poll-on-focus suffices at

launch volume.