MicroservicesMail Gateway

Mail Gateway

The Mail Gateway is the outbound transport worker for prepared email messages. It owns delivery attempts, provider communication, rate limiting, and result publication. It does not render templates, resolve variables, load attachments, or write delivery rows.

The service does not expose HTTP or TCP endpoints. Both directions use durable BullMQ queues in the same Valkey deployment as the backend.

Queue Endpoints

QueueJob nameProducerConsumerPurpose
mailsendBackendMail GatewayDeliver one fully prepared message
mail-resultsresultMail GatewayBackendPersist the final delivery outcome

The backend waits for the result queue transaction to be acknowledged before considering tracked delivery persistence complete. If persistence is unavailable, the Mail Gateway retains the recorded transport result and retries publication without sending the message again.

Functionality

  • Applies the configured sends-per-second limit and optional daily quota.
  • Uses stable job and delivery identifiers for tracked mail.
  • Records attempt progress before contacting the provider.
  • Retries provider failures with exponential delay, up to five attempts.
  • Reports SENT, FAILED, or UNCERTAIN outcomes.
  • Marks an interrupted provider call as UNCERTAIN instead of assuming that it failed.
  • Enforces the configured total attachment byte limit.
  • Publishes final results until the backend acknowledges persistence.

Delivery Providers

EnvironmentProviderConnection
ProductionAWS SES v2AWS SDK through Nodemailer
DevelopmentInbucketSMTP, normally on port 2500

Provider SDK retries are disabled because durable retry ownership belongs to the Mail Gateway. This avoids a hidden provider retry sending a message twice.

Backend Responsibilities

Before enqueueing a mail request, the backend:

  1. Resolves template variables.
  2. Renders the subject, HTML, and plain-text content.
  3. Loads and serializes attachments.
  4. Adds tracking data when required.
  5. Creates or locks the tracked delivery record.

The queue message therefore contains a transport-ready snapshot. The Mail Gateway can deliver it without access to Postgres or the CMS.

Local Start

Start Valkey and Inbucket first, then run:

pnpm mail-gateway:dev

Use packages/mail-gateway/.env.example as the local environment template.