> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# messaging

> 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.

> `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

| Persona                     | Hires this module to                                                                                                 |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Marketing lead** (tenant) | Send a birthday campaign, a points-expiring reminder, or a blast to a segment — and know how many landed.            |
| **Developer** (tenant)      | Have transactional messages just work: an OTP arrives in seconds, a receipt is never blocked by a marketing opt-out. |
| **Member**                  | Get what they asked for and nothing they did not, and change their mind in one click.                                |
| **Us**                      | Ship every other module's notifications without each one inventing its own delivery path.                            |

## 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 `queued` to `opened`, **linked by `correlation_id` back 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

| Measure                                                                             | Target                                           | By                    |
| ----------------------------------------------------------------------------------- | ------------------------------------------------ | --------------------- |
| A marketing blast at full throughput while another tenant's OTPs keep their latency | proven by the `blast-vs-otp` k6 profile          | certification, pre-G1 |
| Transactional delivery latency                                                      | seconds, p95 under load with marketing saturated | certification, pre-G1 |
| Sends reaching a suppressed or non-consenting recipient                             | **zero**, by construction                        | always                |
| Correlation from a domain event to a delivered message                              | traceable end to end in one query                | G1-Engage             |
| Tenant sender domain verified and warmed                                            | under an hour of tenant effort                   | G1-Engage             |

## 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

| Phase   | Contents                                                                                                                                                        | Target                |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| **F1a** | Cascade and channel configuration · templates · sends and status · suppressions and preferences · dual-rail dispatch · email, in-app, webhook and push adapters | G1-Engage, 2026-11-01 |
| **F1b** | Campaigns and triggers · sender domains and warm-up · SMS adapter (behind its provider ADR)                                                                     | post-G1               |
| **F2**  | WhatsApp · Live Activities · cross-channel fallback (DEC-E4) · a template designer if it is asked for                                                           | 2027                  |

## 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.

The main operational risk is provider reputation: one tenant's bad list damages the shared domain
for everyone. Mitigated by warm-up limits, automatic suppression, bounce and complaint thresholds
that pause a sender, and custom domains for volume senders.

## 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.

## Open questions

| # | Question                                                                                           | Decides | By                          |
| - | -------------------------------------------------------------------------------------------------- | ------- | --------------------------- |
| 1 | Bounce and complaint thresholds that automatically pause a tenant's sending — what numbers?        | Daniel  | before FS-MSG-0008 approval |
| 2 | Are open and click tracking on by default, given they require a tracking pixel and link rewriting? | Daniel  | before FS-MSG-0003 approval |

## Changelog

| Version | Date       | Change        | Why | Author                 |
| ------- | ---------- | ------------- | --- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial draft | —   | daniel + claude-opus-5 |
