Notifications
Notification delivery and preference management in NapX PMS.
Notifications
NapX PMS sends transactional notifications for key reservation and access-code events. Delivery is handled asynchronously via a BullMQ queue with automatic retry, and tenants can opt out of specific notification types per channel.
Channel support: email is fully active (AWS SES). sms has provider backing (AWS SNS,
gated by SES_FROM_ADDRESS) and processor dispatch, but no event listeners currently enqueue SMS
notifications. push and in_app are schema-defined but have no provider implementation.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /v1/notifications | List notifications for the tenant (paginated) |
| GET | /v1/notifications/preferences | Get all notification preferences for the tenant |
| PATCH | /v1/notifications/preferences | Upsert a notification preference (opt in or opt out) |
Response Shape — Notification
| Field | Type | Description |
|---|---|---|
id | uuid | Notification ID |
tenantId | uuid | Owning tenant |
recipientId | uuid | null | Recipient user/guest ID; null for system-audience notifications |
recipientType | string | Audience class: user, guest, admin, or system |
templateKey | string | Template identifier (see known templates below) |
channel | string | Delivery channel (e.g. email) |
subject | string | null | Email subject line |
body | string | Rendered notification body |
status | string | Delivery status (pending, sent, failed) |
sentAt | ISO 8601 | null | When the notification was successfully delivered |
createdAt | ISO 8601 string | Enqueue timestamp |
BullMQ Queue with Retry
Notifications are never sent synchronously. When a domain event fires (e.g. reservation checked in),
NotificationListenerService enqueues a job in BullMQ's notifications queue. The processor:
- Calls
db.setTenantContext(job.data.tenantId)to enforce row-level security on all DB reads. - Checks
NotificationPreference— skips delivery if the tenant has opted out for the(templateKey, channel)combination. - Renders the appropriate email template.
- Calls the
NOTIFICATION_ADAPTERto dispatch via SES (email) or SNS (sms). WhenSES_FROM_ADDRESSis unset, aDisabledNotificationAdapterlogs a warning instead of sending.
BullMQ handles backoff and retry automatically for transient failures (SES throttling, network
errors). Permanent failures are logged and the Notification row is updated to status: 'failed'.
Notification Preferences
Preferences allow per-tenant opt-outs at the (templateKey, channel) level. A PATCH to
/v1/notifications/preferences upserts a preference row:
PATCH /v1/notifications/preferences
Authorization: Bearer <session-token>
Content-Type: application/json
{
"templateKey": "checkin_reminder",
"channel": "email",
"enabled": false
}
When a preference with enabled: false exists for a (templateKey, channel) pair, the processor
skips delivery for that combination. When no preference row exists, the notification is sent (all
notifications default to enabled).
For the operator account settings screen, the app updates these email preference templates:
check_in_alertsnightly_summarymarketing_emails
Known Template Keys
| templateKey | Trigger |
|---|---|
checkin_reminder | Reservation checked in |
checkout_summary | Reservation checked out |
reservation_cancelled | Reservation cancelled |
access_code_delivery | Access code created |
Recipient Types
| recipientType | Meaning |
|---|---|
user | A platform user with a user account |
guest | A guest without a platform account |
admin | An admin-level recipient |
system | System-generated notification (no human recipient) |
Security Model
- Every request requires
Authorization: Bearer <session-token>. - Tenant context is derived from validated auth claims; callers only see notifications for their tenant.
Error Handling
400 Bad Requestwhenchannelis not one ofemail,sms,push,in_app.401 Unauthorizedwhen the bearer token is missing or invalid.
Example — List Recent Notifications
GET /v1/notifications?limit=20&offset=0
Authorization: Bearer <session-token>
See the API reference for the detailed endpoint contract.