Notifications
RateNet dispatches transactional emails to every party involved in a booking or billing event. The notification system is event-driven and multi-recipient: a single lifecycle event (e.g. booking confirmed) can send different, contextually appropriate emails to the guest, the client agency, the hotel, the tenant operator, and a system-level superadmin mailbox — all from one trigger.
Architecture overview
Domain event (e.g. booking confirmed)
│
▼
BookingNotifier / StatementNotifier
│
├─ resolveRecipients() ← who gets notified for this event?
│
├─ subject() ← per-recipient subject line
│
├─ templateName() ← {event}_{recipientType}
│
└─ EmailService.SendTemplated() ← async SMTP dispatch
Notifications are fire-and-forget: they are sent asynchronously and never block the booking or billing flow. Errors are logged but not propagated.
Recipient types
| Type | Constant | Who they are |
|---|---|---|
holder | RecipientHolder | The guest/traveller who made the booking |
client | RecipientClient | The client agency's back-office (confirmation email) |
hotel | RecipientHotel | The hotel property contact |
tenant | RecipientTenant | The platform operator running the tenant instance |
superadmin | RecipientSuperadmin | System-level inbox — sees full cross-party context including sell price, cost, and margin |
3-tier email routing
Every party has a master email (required fallback) and optional domain-specific overrides that let you route different notification types to different inboxes.
System level (.env)
This is the superadmin inbox. It receives a copy of every event with full financial context (tenant, client, hotel, sell, cost, margin). It is deliberately not stored in the database.
Tenant level
| Field | Domain | Description |
|---|---|---|
notification_email | Master fallback | Required. Used when no domain-specific email is set. |
reservations_email | reservations | Overrides master for booking lifecycle events |
finance_email | finance | Overrides master for invoice and statement events |
account_management_email | account_management | Reserved for account management notifications |
cc_admins_on_notifications | — | Whether platform admins are CC'd (currently informational) |
Client level
| Field | Domain | Description |
|---|---|---|
confirmation_email | Master fallback | The client's main notification address |
reservations_email | reservations | Overrides for booking confirmations/cancellations |
finance_email | finance | Overrides for statements sent to the client |
Hotel level (direct contracts)
| Field | Domain | Description |
|---|---|---|
hotel_email | Master fallback | The hotel's main contact email |
reservations_email | reservations | Overrides for booking notification to the hotel |
finance_email | finance | Overrides for AP statement notifications |
The EmailForDomain(domain string) string method is implemented on Tenant, Client, and SupplierHotel. It returns the domain-specific email if set, falling back to the master email.
Booking events
Booked through BookingNotifier, triggered by bookingService.NotifyAllParties().
| Event | Constant | Recipient matrix |
|---|---|---|
| Booking Confirmed | EventBookingConfirmed | holder, client, hotel, tenant, superadmin |
| Booking Cancelled | EventBookingCancelled | holder, client, hotel, tenant, superadmin |
| Booking Amended | EventBookingAmended | holder, client, hotel, tenant, superadmin |
| Hotel Confirmed by Hotel | EventHotelConfirmedByHotel | holder, client, hotel, tenant, superadmin |
| Booking Invoiced | EventBookingInvoiced | tenant, superadmin only (internal financial event) |
| Deadline Reminder | EventDeadlineReminder | holder, client, tenant, superadmin (hotel excluded) |
Subject line patterns
| Recipient | Example |
|---|---|
| Holder | Booking Confirmed - RN-00123 |
| Client | New Booking Confirmation - RN-00123 - Grand Hotel |
| Hotel | New Booking - RN-00123 - Grand Hotel |
| Tenant | [Booking Confirmed] RN-00123 - Grand Hotel |
| Superadmin | [System] Booking Confirmed - RN-00123 - Grand Hotel - 1250.00 EUR |
Template naming
Template names follow the pattern {event}_{recipientType}:
booking_confirmed_holder
booking_confirmed_client
booking_confirmed_hotel
booking_confirmed_tenant
booking_confirmed_superadmin
booking_cancelled_holder
booking_cancelled_client
...
deadline_reminder_holder
deadline_reminder_client
deadline_reminder_tenant
deadline_reminder_superadmin
Template data
All booking templates receive a notificationTemplateData struct with:
| Field | Description |
|---|---|
BookingRef | Booking reference number |
HolderName | Guest full name |
HotelName | Hotel property name |
DestinationName / Country | Location fields |
Checkin / Checkout | Dates formatted as YYYY-MM-DD |
BoardName | Board type (e.g. Bed & Breakfast) |
Rooms | Number of rooms |
FinalTotal / FinalCost / Margin | Financial figures — all available in every template |
Currency | ISO currency code |
Deadline | Cancellation deadline (or N/A) |
FreeCancellation | Boolean — true if deadline is in the future |
DaysToDeadline | Days until deadline |
TenantName / ClientName | Party names |
HotelPortalURL | Deep link to hotel portal (hotel templates) |
HotelConfirmationCode | Hotel's own reference number |
ChangeType | Human-readable: New Booking, Cancellation, Amendment, etc. |
Statement events
Handled by StatementNotifier, triggered by statementService.Notify().
| Event | Constant | Recipient matrix |
|---|---|---|
| Statement Created | EventStatementCreated | tenant, superadmin |
| Statement Sent | EventStatementSent | tenant, client (AR only), superadmin |
| Statement Paid | EventStatementPaid | tenant, superadmin |
| Statement Voided | EventStatementVoided | tenant, superadmin |
AR = Accounts Receivable (client owes money). AP = Accounts Payable (we owe the hotel/supplier).
Subject line patterns
| Recipient | Example |
|---|---|
| Tenant | [AR] Statement Created - SOA-202603-001 - Woltravel Agency |
| Client (statement sent) | Statement of Account - SOA-202603-001 - 12500.00 EUR |
| Superadmin | [System][AR] Statement Created - SOA-202603-001 - Woltravel Agency |
Template naming
statement_created_tenant
statement_created_superadmin
statement_sent_tenant
statement_sent_client
statement_sent_superadmin
statement_paid_tenant
statement_paid_superadmin
statement_voided_tenant
statement_voided_superadmin
Deadline reminders
Deadline reminders run as a background task on a configurable schedule per tenant.
Tenant configuration:
| Setting | Default | Description |
|---|---|---|
deadline_reminder_enabled | false | Enables the reminder background task for this tenant |
deadline_reminder_days | 7,3,1 | Comma-separated list of days-before-deadline to trigger reminders |
The DeadlineReminder service queries for confirmed, non-nonrefundable bookings whose deadline falls within a ±12-hour window of each configured tier. Reminders are guaranteed idempotent — each (booking_id, tier_days) pair is recorded in deadline_reminder_sents and will never be sent twice.
Email service
EmailService handles SMTP dispatch with an in-memory audit log.
Configuration (.env):
SMTP_HOST=smtp.example.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=secret
[email protected]
SMTP_FROM_NAME=RateNet
Key behaviours:
- Sending is always asynchronous (goroutine per email)
- 30-second timeout per send attempt
- In-memory audit log (last 10,000 records) accessible via
emailService.GetAuditLog() - Failed emails accessible via
emailService.GetFailedEmails() - If
SMTP_HOSTis not set,SendTemplated()returns an error immediately (no panic)
Adding a new event
- Define a new constant in
booking_notifier.goorstatement_notifier.go - Add recipient logic in
resolveRecipients() - Add subject lines in
subject() - Add template(s) in
email.gofollowing the{event}_{recipientType}naming convention - Call
NotifyBookingEvent()orNotifyStatementEvent()from the service layer