29 · Tenant email for operations
How each business configures outbound mail, how the platform falls back, and which operational events send customer email (receipts, invoices, refunds, store credit).
Transport resolution (per send)
flowchart TD
SEND[sendEmail / OpsEmail] --> T1{Tenant email_settings
provider != none + active?}
T1 -->|yes| USE1[Business SMTP / SendGrid / SES]
T1 -->|no| T2{TicketSettings
emailEnabled + SMTP?}
T2 -->|yes| USE2[Ticket SMTP]
T2 -->|no| T3{Platform env
SMTP_HOST / SMTP_URL?}
T3 -->|yes| USE3[Platform SMTP — shared rail]
T3 -->|no| FAIL[skipped_no_provider + log]
USE1 --> LOG[(email_log tenant main)]
USE2 --> LOG
USE3 --> LOG
Live platform already has SMTP env; tenants without their own settings still send via platform
with GPE From unless they configure branding in email_settings.
HTTP API — /api/email
| Method | Path | Purpose |
|---|---|---|
| GET | /status | canSend, source, provider, from |
| GET | /settings | Read business email config (+ status) |
| POST | /settings | Upsert SMTP/SendGrid/SES for this org |
| POST | /test | Send branded test message |
| POST | /send | Ops template: receipt, invoice, refund, store_credit, void, custom |
| POST | /invoice/:id | Email Core Invoice as receipt/invoice |
| GET | /logs | Recent email_log rows for this org |
Operational templates (opsEmail.service.js)
| Type | When to use |
|---|---|
receipt / invoice | Customer sale document with line items + totals |
refund | Refund confirmation |
store_credit | SC- code after refund-to-credit |
void | Void notice |
test | Settings verification |
custom | Ad-hoc HTML/text |
Product wiring
- Core refund —
POST /api/core-invoicing/refund-invoicereturnsemailNotify(default on; passnotifyCustomer:falseto skip; optionalnotifyEmail) - Receipts —
/api/receipts/:invoiceId/emailand/senduse tenant email service first, platform mailer fallback - Tickets —
ticketNotification.servicealready usesemail.service - Invoice email —
POST /api/email/invoice/:idloads Core invoice + line items
What each business should configure
- Business Hub / Email settings → provider SMTP (or SendGrid/SES)
- From name + From email (aligned with domain SPF/DKIM at their DNS)
- Optional reply-to for customer replies
- Send test to owner inbox
- If left empty: platform SMTP still delivers (shared From) — fine for demos, not ideal for brand/reputation
Platform env
SMTP_HOST, SMTP_USER, SMTP_PASS, MAIL_FROM power fallback.
Never put tenant passwords in platform env; store per-tenant in email_settings.
Files
backend/services/email.service.js— transport resolution + email_logbackend/services/opsEmail.service.js— templates + invoice/refund helpersbackend/services/mailer.js— legacy platform-only helper (still used as last resort)backend/routes/email.routes.js— HTTP APIbackend/routes/receipts.routes.js·coreInvoicing.routes.js— call sites- Related: DocHub 23 tax/receipts/notifications