messaging is how anything in the suite reaches a human. It decides whether a message may be
sent at all, on which channel, renders it, queues it on the right rail, delivers it through a
provider, and tracks what happened to it — all traceable back to the event that caused it.
Who this is for
The problem today
Two problems, one of them ours. The tenant’s problem is that engagement tools treat consent, suppression and quiet hours as campaign settings rather than as gates. A message goes out to someone who unsubscribed last week because the list was exported before they did, and that is a complaint to a regulator, not a bug. Our problem is a hard number from the research: Resend’s rate limit is per team, across every API key — every tenant shares one budget. One tenant’s marketing blast can starve every other tenant’s OTPs and receipts. That finding is why ADR-015 exists and why dual rails are in F1a rather than a scaling concern for later. A platform where one customer’s campaign breaks another customer’s login is not multi-tenant, whatever the database says.What it does (normative)
- Resolves whether and where to send, through a pure cascading function over four configuration levels plus the recipient’s own gates (ADR-019).
- Renders templates with variables validated against the contact’s real attribute definitions.
- Queues on two rails per channel — transactional always outranks marketing (ADR-015).
- Delivers through channel adapters behind a port, so a new channel is an adapter and not surgery.
- Tracks every send from
queuedtoopened, linked bycorrelation_idback to the domain event that caused it. - Maintains suppressions automatically from hard bounces and complaints, checked on every send.
- Gives the member a preference center per channel and per category.
- Runs campaigns: one-shot blasts to a segment, and standing automations on event, date property or segment entry.
Non-goals (normative)
- Not a template designer. Templates are React Email components in the repository, versioned like code. A drag-and-drop builder is a product surface we may want later; it is not what makes messages send.
- Not a deliverability consultancy. We automate SPF/DKIM setup and warm-up, and surface bounce and complaint rates. Reputation is ultimately the tenant’s sending behaviour.
- Not an inbox. Replies go to the tenant’s own address; we are not building a shared mailbox.
- No message content in analytics. We count sends, opens and clicks. What was written stays with the send record and is deleted with the subject on erasure.
- Not WhatsApp or Live Activities in F1. Both are F2, both need a vendor and an ADR (DEC-E1).
Success
Commercial shape
Messages are a metered metric with published unit prices per channel (ADR-016) — email and push cost us different amounts and are priced separately. Enforcement is hard at 110% (DEC-G2), because an unbounded send is an unbounded bill for us. One capability is an add-on: a custom sending domain (DEC-F4). The default is a shared subdomain, which works; a tenant that wants mail from their own domain pays for the setup and the reputation isolation that comes with it.Phases
Compliance and risk
This module is where a consent failure becomes a regulatory event, so consent is not consulted — it is a gate that cannot be bypassed.- Transactional is never opt-out (DEC-E3), and marketing is never sent without an active grant. There is no tenant setting that changes either.
- Suppressions survive erasure as a hashed identifier with no profile attached. Deleting them would let a deleted person be emailed again.
- Message content is personal data. It is deleted with the subject; only aggregate counts remain.
Dependencies
core for contacts, consent and segments — hard, one-way. Resend, FCM, Upstash. QueuePort and
NotificationChannelPort (amendment A1). Every other module depends on this one, and reaches it
only through domain events.