Send email from FastAPI

    Send email from FastAPI with one HTTPS call.

    No smtplib, no MIME assembly, no SMTP config. POST to /v1/messages from httpx, let BackgroundTasks return the response immediately, and get a trace ID per send.

    Start freemailnix Forms

    The job

    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.

    The code

    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

    1. Install httpx and put MAILNIX_TOKEN in your environment or secret store, never in Git.
    2. Verify a sending domain in the mailnix dashboard (SPF + DKIM records).
    3. Paste the snippet: a Pydantic model, an async send helper with an idempotency_key, and the route.
    4. Hit /signup once and look up the returned trace_id in the Traces view.

    What you get on top

    Zero SMTP configuration

    No MAIL_SERVER / MAIL_PORT / MAIL_PASSWORD block, no App Password, no port debate. Bearer token in, JSON out.

    No MIME assembly

    html_body, text_body, and attachments are JSON fields. mailnix builds the MIME parts server-side.

    A trace ID per send

    Every 202 carries a trace_id that resolves to the full delivery timeline: queued, dispatched, delivered, bounced.

    Safe retries

    idempotency_key deduplicates for a rolling window, so a replayed background task never double-sends.

    Gotchas people actually hit

    • Never commit the mnx_live_ token. Read it from os.environ or a secret manager; rotate on leak.
    • Do not await the send inline and do not use smtplib in an async route: queue it with BackgroundTasks so a slow send never blocks the response.
    • BackgroundTasks dies with the process. Move to Celery, RQ, or mailnix scheduled sends once uptime matters.
    • Always pass a timeout to httpx.AsyncClient, and sanitise customer-supplied text before it lands in html_body.

    Send email from FastAPI FAQ

    Can I use smtplib inside an async FastAPI route?

    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.

    Do I need fastapi-mail?

    No. fastapi-mail wraps aiosmtplib and Jinja2. POSTing to the mailnix API from httpx is one dependency instead of five.

    How do I avoid sending the same email twice on a retry?

    Set idempotency_key on the POST body. mailnix deduplicates on that key for a rolling window.

    Which provider does the email actually go through?

    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

    Put a form on your site tonight. Wire your provider when you're ready.