Direct answer
When a portal event must notify a human, write the notification row in the same transaction as the event, then have a worker drain the outbox. Retry with a bound. Mark each channel complete. Surface exhausted failures to an operator.
The Azure Architecture Center’s transactional outbox pattern is the named design. Cloudflare Workers plus a durable store is one implementation; the pattern is the point.
Idempotency
The same decision must not create two ‘accepted’ emails because the user double-clicked. Key the outbox row on (object, event, channel). Providers will still duplicate if you retry without that key — make the provider call idempotent too.
A practical sequence
Persist first, send second.
- Insert the business row and the outbox row together
- Drain on a short cron or queue consumer
- Bound retries; do not loop forever
- Do not log raw message bodies that contain PII
- Show failed deliveries on the operator panel
What not to do
Do not await a third-party HTTP call inside the user’s click without a backup write. Do not swallow the provider error. Do not notify a tenant about another tenant’s object.
What to send if you want a senior review
Share which events must email or chat, and which provider you already have. Related work at Kiwi: SaaS MVP design and development.
Frequently asked questions
Is a queue product (SQS, etc.) required?
A table plus a worker is enough for many v1 portals. Use a dedicated queue when volume or multiple consumers demand it.
Should clients see delivery status?
Show ‘we received this’ on their job. Provider-level bounces belong on the operator panel unless the client must correct an address.
Does this apply to marketing newsletters?
No. This is transactional notification for product events. Marketing consent is a different notice and a different list.
Sources
- Microsoft Azure Architecture — Transactional Outbox pattern
- OWASP — Enforce access controls
- Cloudflare Docs — Workers
Figures cited above are drawn from the linked publications and are the responsibility of their sources; we date and scope them rather than presenting them as universal guarantees.