Skip to main content

Email Provider Migration: Resend → SES → Postmark → Mailgun

Status: COMPLETE for app sends. Mailgun delivers everything the app sends (KineticAI, Sandbox, workspace, broadcasts, automations). No app code calls Resend any more, but Resend is still the SMTP provider Supabase Auth uses for sign-in, signup-confirmation and password-reset email (sender auth@e.kinetic.email), so its DNS records on kinetic.email and e.kinetic.email must stay while that is true. Amazon SES is not used at all — the amazonses.com records under send.* belong to Resend, which runs on SES.

Timeline​

  • Pre-April 2026: Resend as primary email provider (no raw MIME / AMP support)
  • April 2-8: Attempted AWS SES migration for AMP4Email support via raw MIME
  • April 8: SES production access denied twice despite detailed application
  • April 9: Attempted Postmark migration — domain verified, but REST API does not support raw MIME or AMP4Email
  • April 9: Migrated to Mailgun — raw MIME via REST API confirmed working, AMP4Email delivered successfully
  • Post-April 2026: Resend retired from app sending — all sends (AMP and non-AMP) go through Mailgun; api/send-email.ts and RESEND_API_KEY removed. Resend remains only as Supabase Auth's SMTP provider, configured in the Supabase dashboard rather than in this repo

Why SES Failed​

AWS SES requires manual approval to exit sandbox mode. We submitted two detailed applications including full bounce/complaint handling, unsubscribe infrastructure, and CAN-SPAM compliance. Both were rejected with generic responses and no actionable feedback.

Why Postmark Failed​

Postmark's REST API does not support raw MIME (RawSource is undocumented and rejected From headers). Their documentation has no mention of AMP4Email support. The Invalid 'From' value error persisted despite correct Sender Signature configuration. Postmark is not suitable for AMP4Email.

Why Mailgun​

  • Raw MIME via REST API — POST /v3/{domain}/messages.mime accepts full MIME messages as multipart/form-data
  • AMP4Email support — Explicitly documented. text/x-amp-html MIME part works out of the box
  • No sandbox gatekeeping — Domain verification grants immediate sending to any address
  • Free tier — 100 emails/day (~3,000/month), no credit card, no time limit
  • Webhook support — Delivered, failed, complained, opened, clicked events
  • HMAC-SHA256 webhook verification — Signing key for secure webhook authentication

Current Architecture​

ProviderPurposeEndpoints
MailgunAll app sends — AMP4Email (raw MIME) and standard HTMLapi/send-email-mailgun.ts
ResendSupabase Auth email only — magic link, signup confirmation, password resetNone — Supabase → Authentication → SMTP Settings

How It Works​

  • Every send goes through /api/send-email-mailgun (KineticAI, Sandbox, workspace, etc.)
  • Sends require a signed-in user and are attributed to the verified account email
  • The sending domain and from address default to kinetic.email / Kinetic Email <ai@kinetic.email> and can be overridden via MAILGUN_DOMAIN / MAILGUN_FROM without a deploy

Webhook Flow​

Mailgun webhooks → api/mailgun-webhook.ts → logs to email_delivery_events table + feeds bounces/complaints into email_suppressions table → viewable at /station/delivery

Environment Variables​

VariableWherePurpose
MAILGUN_APIVercel + .env.localMailgun API key
MAILGUN_WEBHOOK_SIGNING_KEYVercelWebhook signature verification
MAILGUN_DOMAINVercel (optional)Sending domain override (defaults to kinetic.email)
MAILGUN_FROMVercel (optional)From address override (defaults to Kinetic Email <ai@kinetic.email>)

Removed​

VariableReason
RESEND_API_KEYNo app code sends through Resend — all app sends via Mailgun. Auth email still uses a Resend key, but it lives in Supabase's SMTP settings, not in any app environment
EMAIL_FROMWas Resend's sender address; Mailgun uses MAILGUN_FROM
POSTMARK_SERVER_TOKENPostmark does not support AMP4Email
AWS_ACCESS_KEY_IDSES sandbox access denied
AWS_SECRET_ACCESS_KEYSES sandbox access denied
AWS_REGIONSES sandbox access denied
AWS_SNS_TOPIC_ARNSES sandbox access denied

File Inventory​

FileStatusPurpose
api/send-email-mailgun.tsActiveMailgun send endpoint — all sends, with AMP raw MIME support
api/mailgun-webhook.tsActiveMailgun delivery/bounce/complaint webhook (HMAC-verified)
api/unsubscribe.tsActiveGlobal unsubscribe endpoint (verified session or signed HMAC token)
database/68-email-suppressions.sqlActiveSuppression tables
database/69-email-delivery-events.sqlActiveDelivery event log for Station dashboard

Removed Files​

FileReason
api/send-email.tsResend retired — Mailgun handles non-AMP sends too
api/send-email-postmark.tsPostmark does not support AMP
api/postmark-webhook.tsPostmark removed
api/send-email-ses.tsSES access denied
api/ses-webhook.tsSES removed

AMP4Email Setup​

Sender Requirements​

  • Domain kinetic.email verified in Mailgun with DKIM + SPF
  • DMARC set to p=reject with rua=mailto:dmarc@kinetic.email

Recipient Requirements​

None. kinetic.email is an approved AMP sender for Gmail and Yahoo Mail, and dynamic email is on by default in Gmail. A recipient who has turned dynamic email off (or whose Google Workspace admin has) gets the HTML version.

MIME Structure​

AMP emails are sent as multipart/alternative with three parts:

  1. text/plain — plain text fallback
  2. text/x-amp-html — AMP version (rendered in Gmail, Yahoo)
  3. text/html — HTML fallback (rendered in all other clients)