feat: phase (b) - Postmark, SendGrid, Mailgun and Gmail SMTP #8

Merged
KofTwentyTwo merged 3 commits from feature/phase-b-providers into develop 2026-10-14 12:09:21 +00:00
KofTwentyTwo commented 2026-10-14 02:11:29 +00:00 (Migrated from github.com)

Phase (b) of docs/DESIGN.md section 3.5–3.6. Stacked on #6 (independent of #7).

  • Postmark POST /email (server token, message stream, open/link tracking), SendGrid POST /v3/mail/send (personalizations, custom_args, tracking settings, X-Message-Id), Mailgun POST /v3/{domain}/messages.mime (US/EU, multipart built in memory, Bcc stripped from the MIME and passed as recipients).
  • One EmailHttp seam over java.net.http (10 s connect, 30 s request, HTTPS only, no redirects).
  • Error mapping: per-message rejections permanent; 429/5xx transient with Retry-After; bad or refused credentials and account-level errors provider-level (provider goes unhealthy, messages wait). Response bodies are never stored; fixed messages plus the provider's code.
  • Gmail: GMAIL_SMTP preset (smtp.gmail.com:587 STARTTLS, account and app-password env names required); GMAIL_API refused with a pointer to the preset, as specified.
  • Per-kind validation and default env names (POSTMARK_SERVER_TOKEN, SENDGRID_API_KEY, MAILGUN_API_KEY, GMAIL_USERNAME, GMAIL_APP_PASSWORD, webhook secrets).
  • docs/PROVIDERS.md: setup per provider, env names, limits, Gmail app passwords and daily limits.

Tests: 113 with #6, all green (request shapes, id parsing, every error class, secrets absent from errors, an end-to-end send per provider against a local HTTP server, Gmail validation). JaCoCo 94.5% instructions / 100% classes; Checkstyle clean.

🤖 Generated with Claude Code

Phase (b) of `docs/DESIGN.md` section 3.5–3.6. Stacked on #6 (independent of #7). - **Postmark** `POST /email` (server token, message stream, open/link tracking), **SendGrid** `POST /v3/mail/send` (personalizations, custom_args, tracking settings, `X-Message-Id`), **Mailgun** `POST /v3/{domain}/messages.mime` (US/EU, multipart built in memory, Bcc stripped from the MIME and passed as recipients). - One `EmailHttp` seam over `java.net.http` (10 s connect, 30 s request, HTTPS only, no redirects). - Error mapping: per-message rejections permanent; 429/5xx transient with Retry-After; bad or refused credentials and account-level errors provider-level (provider goes unhealthy, messages wait). Response bodies are never stored; fixed messages plus the provider's code. - **Gmail:** `GMAIL_SMTP` preset (smtp.gmail.com:587 STARTTLS, account and app-password env names required); `GMAIL_API` refused with a pointer to the preset, as specified. - Per-kind validation and default env names (`POSTMARK_SERVER_TOKEN`, `SENDGRID_API_KEY`, `MAILGUN_API_KEY`, `GMAIL_USERNAME`, `GMAIL_APP_PASSWORD`, webhook secrets). - `docs/PROVIDERS.md`: setup per provider, env names, limits, Gmail app passwords and daily limits. Tests: 113 with #6, all green (request shapes, id parsing, every error class, secrets absent from errors, an end-to-end send per provider against a local HTTP server, Gmail validation). JaCoCo 94.5% instructions / 100% classes; Checkstyle clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.