#!/usr/bin/env python3 """ Test outgoing Mailgun credentials by sending a single email via the HTTP API. Usage: MAILGUN_API_KEY='...' MAILGUN_FROM='noreply@example.com' \\ ./mailgun_test.py recipient@example.com Env vars: MAILGUN_API_KEY (required) — Mailgun API key MAILGUN_FROM (required) — sender address (must be on a verified domain) MAILGUN_DOMAIN (optional) — defaults to the domain part of MAILGUN_FROM MAILGUN_BASE_URI (optional) — defaults to https://api.mailgun.net/v3 use https://api.eu.mailgun.net/v3 for EU region Prints HTTP status + Mailgun response body. Mailgun returns JSON with an "id" + "message" on success, or an error string on failure (e.g. bad domain, unverified sender, invalid key). """ import base64 import json import os import sys import urllib.error import urllib.parse import urllib.request from email.utils import parseaddr def main() -> int: if len(sys.argv) != 2: print(f"usage: {sys.argv[0]} recipient@example.com", file=sys.stderr) return 2 recipient = sys.argv[1] api_key = os.environ.get("MAILGUN_API_KEY") sender = os.environ.get("MAILGUN_FROM") if not api_key or not sender: print("error: MAILGUN_API_KEY and MAILGUN_FROM env vars are required", file=sys.stderr) return 2 _, sender_addr = parseaddr(sender) domain = os.environ.get("MAILGUN_DOMAIN") or sender_addr.split("@", 1)[-1] base_uri = os.environ.get("MAILGUN_BASE_URI", "https://api.mailgun.net/v3") url = f"{base_uri.rstrip('/')}/{domain}/messages" data = urllib.parse.urlencode({ "from": sender, "to": recipient, "subject": "Mailgun test from mailgun_test.py", "text": f"This is a test message sent via {url} as {sender}.\n", }).encode() auth = base64.b64encode(f"api:{api_key}".encode()).decode() req = urllib.request.Request(url, data=data, method="POST") req.add_header("Authorization", f"Basic {auth}") req.add_header("Content-Type", "application/x-www-form-urlencoded") print(f"POST {url}") print(f" from={sender} to={recipient}") try: with urllib.request.urlopen(req, timeout=30) as resp: body = resp.read().decode() print(f"\nHTTP {resp.status}") try: print(json.dumps(json.loads(body), indent=2)) except json.JSONDecodeError: print(body) print(f"\nOK: Mailgun accepted the message") return 0 except urllib.error.HTTPError as e: body = e.read().decode(errors="replace") print(f"\nHTTP {e.code} {e.reason}") print(body) return 1 if __name__ == "__main__": sys.exit(main())