Communication — End-user view
The part users feel most directly: they receive emails, push messages, and in-app messages. Below are familiar experiences and what sits behind them.
1. Receiving messages through multiple channels
"I receive a booking-confirmation email and also see a notification in the app."
One event sends through multiple channels at the same time: email, push, SMS, in-app. Each recipient / channel is a separate OutgoingMessage — the system knows exactly whether each channel has arrived.
2. Content fills in the right information
"The email uses my name, the right booking code, and the right date."
Message content is based on a template with placeholders such as {{TÊN}}, {{MÃ_BOOKING}}. At send time, the system fills real data — each recipient gets personalized content without manual copywriting.
3. No duplicate messages
"The system seems to process my order again, but I only receive one email."
Before sending for real, the system claims each message. A message already "sending/sent" → repeat processing exits without calling the provider again. Result: no duplicate emails.
4. In-app inbox
"I open the app and see my notifications; parents see messages grouped for their children."
In-app messages are stored as UserMessage — one row per recipient. Parents see a combined inbox of notifications related to their children.
Inbox operations
- The inbox is read-only: only read, archive, save/unsave; no reply, thread, compose,
New message, or recipient search — content is produced by the producer, there is no endpoint to create messages from the inbox. - Messages are categorized by
UserMessageCategory(Reminder/Notification/Message); filtering is a server-side filter, not a client-side scan; each tab has its own unread from a server breakdown. - Unread badge: total + per category appears at the entrypoint when it renders (visible before opening the panel); archived messages do not count; a tab with no unread → no badge. The panel prefetches the first page on open and stays mounted during the session.
- Retention: unsaved messages are deleted after 3 months from
CreatedOn(regardless of read/archive); saved messages are exempt; save/unsave is idempotent and does not touch other state. - Authorization by portal: Admin Portal =
EnterpriseUser+ currentEmployee; Enterprise Portal =EnterpriseUser+ all linked Employee profiles; Customer Portal / AimyMe mini-app = current Account; AimyMe Global View = all linked Accounts (no need to pick one). Messages out of scope → not-found (like nonexistent messages). - Privacy: list returns only a bounded safe preview; body streamed once authorized; no permanent Blob URLs exposed; logs/metrics contain no body or recipient data.
Real-world example: A parent reads the
StaffFirstAidExpirationreminder, saves the holiday-campaign message, then the producer retries both. → The inbox does not change: the reminder stays read, the holiday message stays saved, no duplicate rows.
5. Campaigns & recurring reports
"The center sends a holiday notice to all parents." / "I receive a report every week."
- Campaign: bulk send to many recipients at once.
- Report subscription: automated report on a schedule.
Both use MessagingJob + Blob to handle large data, then pass through the same send pipeline.
Summary for end-users
| Experience | Powered by |
|---|---|
| Receive messages by email/push/SMS/in-app | Multiple OutgoingMessage / UserMessage records by channel |
| Content has the correct name and data | Template + render in Generator |
| No duplicate messages | Claim before send (send claim) |
| In-app inbox, grouped for parents | UserMessage partitioned by recipient |
| Read-only inbox: read/archive/save, 3-month retention | UserMessage + inbox API authorized by portal scope |
| Bulk notice / report delivery | Campaign / Report subscription |
Want the mechanism underneath? Read Core concepts.