Project ROUNDTABLE Docs
Data model

Notifications and audit

User alerts, preferences, and accountability records

Notifications are user-facing work queues; audit logs are accountability records. A notification tells someone something happened. An audit log records that the application accepted an authentication, authorization, publishing, routing, or administrative action.

Notification

ColumnPrisma typeRequired?DefaultMeaning
idStringYescuid()Notification identifier.
userIdStringYesNoneRecipient.
kindNotificationKindYesNoneCategory used by the bell and settings.
titleStringYesNoneShort alert title.
bodyStringYesNoneHuman-readable details.
linkUrlString?NoNULLOptional in-app destination.
readBooleanYesfalseWhether the recipient marked it read.
createdAtDateTimeYesnow()Creation time.

The user relation enforces recipient ownership. Notification list/read endpoints always filter by the current user's ID.

NotificationKind

ValueMeaning
SUBMISSION_MATCHA submission matched a portfolio or interest profile.
ANNOUNCEMENTA government announcement was published.
CALL_OPENEDA call for technology became available.
ENGAGEMENT_UPDATEAn engagement-related update.
SYSTEMSystem or administrative notice.

NotificationSetting reminder

ColumnPrisma typeRequired?DefaultMeaning
idStringYescuid()Preference identifier.
userIdStringYesNonePreference owner.
kindNotificationKindYesNoneCategory.
enabledBooleanYestrueOn/off switch.
techTagsStringYes""Optional event-tag filter.

The unique pair (userId, kind) lets PUT settings safely upsert. A missing row means enabled by default.

AuditLog

ColumnPrisma typeRequired?DefaultMeaning
idStringYescuid()Audit identifier.
timestampDateTimeYesnow()Time of recorded event.
eventStringYesNoneStable event label such as submission_routed.
userEmailString?NoNULLActor or affected account email when available.
ipString?NoNULLSource IP when the route captures it.
detailString?NoNULLOptional JSON string with operational context.

Audit logs intentionally do not use an enum: new operational events can be added without a migration. Current event labels include authentication_success, authentication_failure, authorization_failure, registration_rate_limited, registration_conflict, industry_registration, org_profile_viewed, org_profile_update, submission_created, submissions_viewed, submission_file_downloaded, submission_routed, submissions_rerouted, search_performed, notification_settings_updated, notifications_mark_read, announcement_published, call_opened, call_status_change, engagement_created, engagement_updated, engagement_deleted, engagement_note_created, org_dialogue_note_created, org_status_change, admin_commands_viewed, admin_command_viewed, admin_command_create, admin_command_update, admin_command_delete, admin_command_reparent, admin_users_viewed, admin_user_create, admin_user_update, admin_user_role_change, export_csv, export_all, export_ics, password_change_rate_limited, password_change_failure, and password_change_success.

Notification flow

notifyUser, notifyUserForTags, notifyGovByTags, and notifyAllIndustry centralize fan-out policy. They create database rows rather than sending external email. This makes the in-app bell deterministic and auditable.

Why audit is not a notification

An audit record has an actor email, optional IP, event label, and operational detail. It is intended for reviewers and operators. A notification has a recipient, title, body, link, read state, and category. It is intended for a user's work queue. A successful action may produce both, only one, or neither depending on route purpose.

Audit detail is JSON serialized text in the current schema. Consumers should parse it defensively because event types do not share one fixed shape. The event label is the stable first filter; detail is supplementary context such as IDs, previous/new parents, match counts, or notification counts.

The schema has no soft-delete flag on audit rows. Application code creates rows but should not treat them as a complete tamper-proof log without database access controls and retention policy.