At a Glance
Owner: Workflow runtime (writes) + Sysadmin / Platform Admin (templates & news) · Table:tb_notification(+tb_message_format,tb_news) · Used by: every workflow stage transition · The inbound message pipe — inbox, templates, platform bulletins.
The notification entity is the inbound message pipe — every workflow stage transition, comment mention, system bulletin, and platform announcement is materialised so the app shell can render a badge + inbox drawer, and optionally dispatch via email, SMS, or push.
Three platform-schema tables collaborate: tb_notification (row-per-message inbox), tb_message_format (reusable templates with channel flags is_email / is_sms / is_in_app), and tb_news (platform-wide bulletins). All live in platform schema because notifications cross BU boundaries and broadcasts must reach every tenant.
Maintained by workflow runtime (writes per-event), Sysadmin (templates), Platform Admin (news). Read by the app shell inbox and dashboard widget.
| Task | Where | Notes |
|---|---|---|
| View inbox | App shell → notification bell | Filtered to to_user_id = me |
| Mark as read | Click message | Updates is_read = true |
| Edit a message template | Sysadmin → Platform Config → Message Formats | Affects every event using that format |
| Post a platform bulletin | Platform Admin → News | Visible to every tenant immediately |
| Schedule a notification | Set scheduled_at |
Dispatcher fires when schedule passes |
| Resend dispatch | Re-process via outbound channel | is_sent tracks per-channel delivery |
| Symptom | Cause | Action |
|---|---|---|
| User did not receive expected notification | Recipient resolution missed; or is_in_app = false on the format |
Check tb_message_format channel flags + workflow recipient map |
| Duplicate inbox rows for one event | Recipient set contained duplicates | App must dedup before insert |
| Template name conflict | tb_message_format.name exists among non-deleted |
Reactivate or pick different name |
| Email dispatched but in-app missing | is_in_app = false on format |
Toggle on if both channels needed |
| Click-through 404 | metadata references a deleted tenant entity |
No FK enforcement; UI must handle gracefully |
metadata may carry tenant identifiers (e.g. PR id). Platform schema enforces no FKs into tenant tables.tb_news has no BU scoping — every authenticated user across every tenant sees rows. Use targeted in-app for tenant-specific.from_user_id IS NULL.is_read owned by recipient — writer never updates it.Source: platform schema.
tb_notification| Field | Prisma Type | Nullable | Description |
|---|---|---|---|
id |
String @db.Uuid |
No | Primary key. |
from_user_id / to_user_id |
String? @db.Uuid |
Yes | FKs to tb_user. from = NULL = system. |
type |
String @db.VarChar(255) |
No | Default SYS_INFO. Discriminator (SYS_INFO, BU_INFO, PR, PR_COMMENT, SR, SR_COMMENT, …). |
category |
String @db.VarChar(255) |
No | Default system. system or user-to-user. |
title / message |
String? |
Yes | Display text. |
metadata |
Json? @db.JsonB |
Yes | Source context (entity_id, route, event id). |
is_read / is_sent |
Boolean? |
Yes | Default false. |
scheduled_at |
DateTime? |
Yes | Defer-until timestamp. |
| Audit columns | — | Yes | created_*, updated_*, deleted_*. |
tb_message_format| Field | Prisma Type | Nullable | Description |
|---|---|---|---|
id |
String @db.Uuid |
No | Primary key. |
name |
String @db.VarChar |
No | Format name (e.g. pr_stage_advanced). |
message |
String? |
Yes | Template body with interpolation tokens. |
is_email |
Boolean |
No | Default false. |
is_sms |
Boolean? |
Yes | Default false. |
is_in_app |
Boolean? |
Yes | Default true. Materialise inbox row. |
| Audit columns | — | Yes | created_*, updated_*, deleted_*. |
Constraints: @@unique([name, deleted_at]).
tb_news| Field | Prisma Type | Nullable | Description |
|---|---|---|---|
id |
String @db.Uuid |
No | Primary key. |
title |
String @db.VarChar |
No | Bulletin title. |
contents |
String? @db.VarChar |
Yes | Body. |
url / image |
String? @db.VarChar |
Yes | Optional read-more URL / banner. |
| Audit columns | — | Yes | created_*, updated_*, deleted_*. |
tb_notification rows. App responsible for dedup.tb_message_format flags decide which channels attempt delivery; is_sent records outcome.name unique among non-deleted; recover via reactivate or insert under new name.is_sent only when scheduled_at fires.from_user_id / to_user_id resolution.../carmen-turborepo-backend-v2/packages/prisma-shared-schema-platform/prisma/schema.prisma — tb_notification (lines ~267-301), tb_message_format (lines ~226-245), tb_news (lines ~690-703).../carmen/docs/workflow-permissions-system.md — role types driving notifications.