Skip to main content

Mailgun Integration

Mailgun is the email delivery provider for Kinetic Email. (Resend was fully retired — all sends now go through Mailgun.)

Setup

  1. Create a Mailgun account and verify the sending domain
  2. Set environment variables:
MAILGUN_API=your-api-key                              # required
MAILGUN_DOMAIN=kinetic.email # optional override (default: kinetic.email)
MAILGUN_FROM="Kinetic Email <ai@kinetic.email>" # optional override
MAILGUN_WEBHOOK_SIGNING_KEY=your-signing-key # required for webhooks (fails closed)

Emails are sent from Kinetic Email <ai@kinetic.email> by default. The domain and From address are env-overridable so a move to a dedicated sending subdomain is a Vercel env change, not a deploy.

How It's Used

The /api/send-email-mailgun endpoint sends all email as raw MIME via Mailgun's messages.mime REST API — this supports the text/x-amp-html MIME part required for AMP4Email:

POST https://api.mailgun.net/v3/{MAILGUN_DOMAIN}/messages.mime
Authorization: Basic base64("api:" + MAILGUN_API)

Authentication Requirement

The send endpoint requires a verified user session (verifyUser, Bearer JWT). The sender email is always the verified session user — it is never taken from the request body, which prevents spoofing the "sent to you by" attribution banner.

Suppression Enforcement

Every send is checked against the suppression system first:

  • email_suppressions — hard bounces, complaints, and manual unsubscribes are rejected outright
  • Soft-bounce cooldowns — a bounce in the last 24 hours blocks sending (with retry_after), and 3 soft bounces within 7 days triggers a 30-day suppression

Delivery Webhooks

Mailgun POSTs delivery events to /api/mailgun-webhook. HMAC-SHA256 signature verification is mandatory: if MAILGUN_WEBHOOK_SIGNING_KEY is unset the endpoint fails closed, and unsigned or badly signed requests are rejected with 401 — otherwise anyone could POST forged bounce events and poison the suppression list.

EventHandling
delivered, opened, clickedLogged to email_delivery_events
failed (permanent)Recipient permanently suppressed + Telegram alert
failed (temporary)Logged to email_bounce_log; 4+ consecutive escalates to permanent suppression
complainedRecipient permanently suppressed + Telegram alert

See Email Sending API for the full request/response reference.