Skip to content

Goal: Direct Email Sending for Email Orders

Enable users to send an Email Order directly from the system — from the Order Queue’s Email Order composer (the EmailPanel) — through the now-shipped ShopAccess/Email module, instead of copying the message to the clipboard and sending it from their own mail client. This is the Email-Orders half of the Enable Direct email sending for Orders effort.

RepositoryRolePlanned Changes
arda-frontend-appProduction frontendAdd a Send action to the EmailPanel; replace the stub POST /api/email/send-order BFF route with one that calls the backend job endpoint; supplier-recipient handling and not-provisioned UX.
ux-prototypeDesign systemSend-action UI components if the panel needs them (only if needed).
documentationProject docsThis project directory: the UI design build-up (email-order-ui.md), the integration design.md, and its decision-log.md.
  1. From the Email Order panel, a user can send the order email directly to the supplier without leaving the system (no copy + paste).
  2. The send is performed server-side via POST /v1/shop-access/email/job (real delivery), not the current logging-only stub route.
  3. The composed message (greeting, items table, signature) is delivered as the email body (htmlBody / textBody) per the backend contract.
  4. The recipient is the supplier’s email address; the UI handles a missing or unknown supplier email gracefully.
  5. Sending is gated on the tenant having a sendable EmailConfiguration; when none exists, the UI explains why and points to provisioning (depends on PDEV-971) rather than failing opaquely.
  6. Delivery acceptance and error states (suppression, not-sendable) are surfaced to the user.

In arda-frontend-app, the Order Queue (/order-queue) opens the EmailPanel slide-over when ordering items whose supplier order method is EMAIL. Today it composes a formatted order email but only offers Copy to clipboard; the BFF route POST /api/email/send-order is a stub that logs generated HTML and never reaches any backend. The backend send capability now exists and is deployed to production. See the UI design build-up in email-order-ui.mdx and the Email module API reference.

The integration is designed against the deployed Email module contract — see design.md for the full treatment:

  • SendPOST /v1/shop-access/email/job takes an EmailJobInput (configurationEId, recipients {to, cc, bcc}, subject, htmlBody / textBody, replyToEmail, attachments) with required X-Tenant-Id and Idempotency-Key headers, and is idempotent on that key. There is no from field — the sender is fixed by the configuration.
  • Capability detectionPOST /v1/shop-access/email/configuration/query lists the tenant’s configurations. Direct send is enabled when one is in Operational state and its identity.sendingDomainSlug contains the procurement marker (a single, easily-updated front-end constant). Resolved at sign-in / tenant switch and cached in the SPA.
  • Field mapping — the composer’s recipients map to to / cc; the UI From maps to Reply-To (replyToEmail); an editable Subject (default Order for {supplier} — {MMM d, yyyy}) supplies the required subject; the rendered body is composed as inline-styled HTML plus a plain-text alternate.
  • A Send-by-email action on the Email Order composer, gated by the cached direct-send toggle. Send is a split button offering HTML (default) or plain text, mirroring the two versions the dual-format Copy provides.
  • A BFF config-status route that resolves and caches the per-tenant toggle, and a real BFF send path that builds the EmailJobInput (recipients, subject, HTML/text body, Reply-To, Idempotency-Key) and calls the backend job endpoint.
  • Client-side validation (≥1 To, well-formed addresses, escaped content) with blocking error feedback, and success/error toasts.
  • UX for the not-provisioned / not-sendable (restricted, copy-only) case.
  • PO-Line order emails — tracked in PDEV-970.
  • Provisioning tenant email configurations — tracked in PDEV-971.
  • Per-send From override / multi-From (deferred to PDEV-903 at the backend).
  1. The From address is fixed by the configuration’s signature — no per-send override.
  2. Cross-Universe rule: tenant/user references stay soft (no cross-service foreign keys or shared transactions).
  3. Reuse the existing EmailPanel message composition (greeting, items table, signature) rather than rebuilding the email body.
  4. Send requests require an Idempotency-Key (minted per Send, stable across retries) and an X-Tenant-Id (injected by the BFF from the session).
  5. Direct send is offered only when the tenant has an Operational procurement EmailConfiguration; the marker slug lives in a single, easily-updated front-end constant.
  6. User-entered content is HTML-escaped at compose time; the email body is never the raw rendered DOM.
  7. The shared BFF send route allow-list-sanitizes the HTML body and rejects the request if anything is stripped — an HTML/script-injection defense shared by all clients (e.g. the upcoming PO direct send). Backend-side hardening is tracked by PDEV-976.

Direct send lets an authenticated user send email from the tenant’s verified domain with editable recipients and body. The front-end / BFF injection defenses are specified in design.md (§Security) and the backend hardening in PDEV-976. The following are product / security decisions still open:

  1. Recipient scope — constrain To/Cc to the supplier’s known contact address(es) (allow-list), or allow free-form entry with an audit trail? Free-form sending from a verified domain is a spam / phishing / data-exfiltration vector.
  2. Authorization to send — which role(s) may send directly? It is a higher-privilege action than copy-to-clipboard and may warrant a separate permission gate.
  3. Reply-To policy — allow an arbitrary Reply-To (the editable From → Reply-To, DQ-004), or constrain it (e.g. to the sending user or the tenant domain) to limit reply-redirection phishing?
  4. Rate / volume limits — per-tenant send caps to protect deliverability, domain reputation, and cost (also a backend item in PDEV-976).

These likely need product + security review before implementation locks.