Notifications
The messages Ulsav sends, the channels behind them, retries, and what preferences do today
Ulsav sends a small set of business messages: an Order receipt, a Pass with its credential link, an Event cancellation notice, a scheduled report digest, and a sign-in code. This page explains when each one goes out, how retries work, and which controls are real today.
Business messages
Four
Receipt, Pass, cancellation, digest
Sign-in
One-time code
Channels
Email, WhatsApp, SMS
Retries
Up to 6 attempts
Exponential backoff, capped at 1 hour
Digest
Email only
Blocking
Never
A failed send cannot undo a sale
What is sent
| Message | Trigger | Channels |
|---|---|---|
| Order confirmation | An Order confirms | Email, WhatsApp, SMS |
| Pass issued to holder | A Participant place is issued with a credential link | Email, WhatsApp, SMS |
| Event cancelled | An organizer cancels an Event | Email, WhatsApp, SMS |
| Report digest | A schedule's send time arrives | Email only |
| Sign-in code | Someone signs in | Email, WhatsApp, SMS |
Each message has a fixed shape:
- The receipt lists pass lines, tax components, the platform fee, the fee tax, and the total.
- The Pass message carries the Event details and a credential URL.
- The cancellation message carries the reason and the amount refunded so far.
- The digest lists summary metrics.
The cancellation message reads the refund total at send time. If the money has not moved yet, the message shows zero and treats the refund as still in progress, rather than promising money that has not been returned.
How sending works
A business action writes a notification request in the same transaction as the action itself. That request holds identifiers only: who receives it, what it is about, a dedupe key, and an optional host for links.
A background dispatcher then turns the request into one delivery per available channel:
- Email, when the recipient has an email address.
- WhatsApp or SMS, when the recipient has a phone number. Which one is a server setting.
The stored destination is masked. The full address is resolved again at send time.
A failed message never rolls back a sale
The Order, the Registration, and the CheckIn commit before any message is attempted. If email is down, the sale still stands and the delivery is retried. This is why delivery trouble never blocks a door.
Deduplication and retries
Every request has a dedupe key. Replaying the same business event does not create a second message.
A send is classified as terminal or retryable.
| Outcome | What happens |
|---|---|
| Sent | Delivery marked sent, with the provider message id |
| Retryable failure | Backoff, then try again, up to 6 attempts |
| Terminal failure | Delivery marked failed, no more attempts |
| No destination | Delivery suppressed |
Backoff starts at 30 seconds and doubles, capped at one hour. Each attempt is recorded with its provider, status, error class, and time. A delivery ends as pending, sending, sent, failed, or suppressed.
Channels and templates
Email renders a subject, HTML body, and plain-text body from a shared template.
WhatsApp and SMS use a canonical template key and positional parameters. WhatsApp requires a template approved by the provider, so the key and the parameter order matter. SMS can send plain text as well.
The provider is chosen per channel by configuration. One adapter is active for email, one for WhatsApp, and one for SMS. A missing credential fails at send time, not at boot.
Preferences
A Member has a preference row per Organization, category, channel, and enabled flag. Channels in the table include email, WhatsApp, SMS, and in-app.
The list can be read and one row can be written at a time. An absent row means no explicit choice.
One important limit: the send path does not consult these preferences today. A stored preference is a record of intent, not a filter. Every business message is sent on every channel where the recipient has an address.
That is acceptable while the message set is transactional. It is not a marketing subscription system.
Digest consent
Digests are the one place consent is enforced. A Member becomes a digest recipient when they are added to a report schedule. That records email consent for the digest purpose.
The scheduled runner checks consent before creating a delivery. No consent row means no digest, even when the person is listed on the schedule.
There is no self-serve unsubscribe control in the product today. Removing someone from the schedule is the working way to stop their digest. External email addresses on a schedule are accepted by the form but are not delivered.
What is not available
- There is no three-tier model in the shipped code. There are business messages and opt-in digests, not transactional, operational, and marketing tiers.
- There are no optional operational alerts such as "new Order" or "low Pass availability". Those are not message types.
- There is no marketing or newsletter sender.
- Stored preferences do not suppress any current message.
- There is no per-category template registry an organizer can extend.
- There is no retry dashboard. Delivery and attempt rows exist, but there is no organizer screen dedicated to failed sends.
Why the limits make sense
The current messages are part of a transaction the recipient started, or a report they asked to receive. Getting a receipt for a purchase is not a subscription choice, and silencing it would hide money that moved.
As optional alerts arrive, they will need a real preference check before send. The table and the API are already there; the enforcement is the missing piece.
Related
- Reports — scheduled delivery
- Orders — the receipt source
- Participants — the credential link in the Pass message
- Members — who receives a digest