The usual options are smtplib (blocks the event loop), aiosmtplib (hand-assembled MIME parts), or fastapi-mail (a wrapper with a five-variable ConnectionConfig). mailnix replaces all of them with one JSON POST, and every send returns a trace_id you can look up when a receipt goes missing.
main.py
import os
import httpx
from fastapi import BackgroundTasks, FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
MAILNIX_URL = "https://api.mailnix.ch/v1/messages"
MAILNIX_TOKEN = os.environ["MAILNIX_TOKEN"] # mnx_live_..., from a secret store
class SignupIn(BaseModel):
email: EmailStr
name: str
async def send_welcome_email(to: str, name: str) -> None:
payload = {
"from": "hello@yourdomain.com",
"to": [to],
"subject": f"Welcome, {name}",
"html_body": f"<p>Hi {name}, thanks for signing up.</p>",
"text_body": f"Hi {name}, thanks for signing up.",
"idempotency_key": f"welcome:{to}",
}
headers = {"Authorization": f"Bearer {MAILNIX_TOKEN}"}
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(MAILNIX_URL, json=payload, headers=headers)
resp.raise_for_status()
# resp.json() carries the trace_id; log it for your audit trail.
@app.post("/signup", status_code=202)
async def signup(body: SignupIn, background_tasks: BackgroundTasks):
# Queue the send; the response returns before the email goes out.
background_tasks.add_task(send_welcome_email, body.email, body.name)
return {"status": "queued"}Setup, in order
No MAIL_SERVER / MAIL_PORT / MAIL_PASSWORD block, no App Password, no port debate. Bearer token in, JSON out.
html_body, text_body, and attachments are JSON fields. mailnix builds the MIME parts server-side.
Every 202 carries a trace_id that resolves to the full delivery timeline: queued, dispatched, delivered, bounced.
idempotency_key deduplicates for a rolling window, so a replayed background task never double-sends.
You should not. smtplib blocks the event loop during the SMTP handshake. Use httpx with an HTTP API, and BackgroundTasks so the response returns first.
No. fastapi-mail wraps aiosmtplib and Jinja2. POSTing to the mailnix API from httpx is one dependency instead of five.
Set idempotency_key on the POST body. mailnix deduplicates on that key for a rolling window.
Whichever destination you connect: SES, Postmark, Resend, SendGrid, Mailgun, or your own SMTP. Routing and failover happen in mailnix, with no change to your FastAPI code.
One HTTP call to get started