Skip to content

Mailgun

[endpoints.transport]
type = "mailgun"
[endpoints.transport.settings]
api_key = "${env.MAILGUN_API_KEY}"
domain = "mg.yourdomain.com"
SettingRequiredDescription
api_keyyesMailgun account API key. Used as HTTP Basic password (username is the literal string api).
domainyesMailgun sending domain (e.g., mg.yourdomain.com). Appears in the API path as /v3/<domain>/messages.
base_urlnoAPI host override. Default is https://api.mailgun.net. Set to https://api.eu.mailgun.net for the EU region.
AspectBehavior
API endpointPOST https://api.mailgun.net/v3/<domain>/messages (or EU equivalent)
AuthenticationHTTP Basic — username api, password = api_key
Body formatmultipart/form-data (constructed via mime/multipart.Writer)
RecipientsRepeated to fields, one per recipient
Reply-ToMailgun’s h:Reply-To custom-header convention
Per-request timeout5s (transport-level)
transport_message_idParsed from response id field (Mailgun returns <msg-id@yourdomain.com> shape)

Mailgun has separate US and EU infrastructures. Operator picks via base_url:

[endpoints.transport.settings]
api_key = "${env.MAILGUN_API_KEY}"
domain = "mg.yourdomain.com"
base_url = "https://api.eu.mailgun.net" # EU region

The API keys are region-scoped — a US-region key won’t work against the EU endpoint and vice versa.

Mailgun responseError classRetry?
200 OK(success)no
429 Too Many RequestsErrRateLimitedyes, after 5s
5xx server errorErrTransientyes, after 1s
4xx other than 429 (400, 401, 403)ErrTerminalno

Mailgun’s error body is {"message": "..."} — Posthorn surfaces this in terminal-failure logs.

  • SPF authorizing Mailgun (include:mailgun.org)
  • DKIM record published from Mailgun’s dashboard
  • MX records for inbound (only if you also use Mailgun’s inbound routing — not relevant for outbound-only Posthorn deployments)
SymptomLikely causeFix
HTTP 401 UnauthorizedWrong region (US key against EU endpoint or vice versa)Verify the API key matches base_url’s region
HTTP 400 'from' parameter is not a valid addressMailgun’s parser is strict about display-name + address formatsUse Name <addr@domain> form; avoid extra quoting
5xx during high-volume sending burstsMailgun rate-limits per-domain — Posthorn’s 429 retry handles short burstsIf sustained, tune the operator-side rate limit or upgrade Mailgun plan
Recipients in sandbox restrictionFree tier, recipient not in authorized listAdd recipient in Mailgun dashboard or upgrade