backend beginner

Transactional Email with Resend

min read Frederick Tubiermont

Transactional Email with Resend

Every real app needs to send email — welcome messages, password resets, order confirmations. This tutorial shows how to integrate Resend into Flask cleanly, keeping your routes thin and email logic in a reusable helper.

Why Resend?

Resend is a modern email API built for developers:

  • Simple Python SDK — one function call to send
  • Reliable delivery with SPF/DKIM handled automatically
  • Free tier — 3,000 emails/month, 100/day
  • Dashboard with delivery logs and per-email analytics

No SMTP port confusion, no TLS certificate setup, no MX records to manage yourself.

Setup

Install the SDK:

pip install resend

Add your API key to .env:

RESEND_API_KEY=re_your_key_here
[email protected]

Get your API key at resend.com → API Keys → Create API Key.

Create the Email Helper

Create utils/email.py:

import os
import resend
from flask import render_template_string

resend.api_key = os.environ.get("RESEND_API_KEY")
FROM_EMAIL = os.environ.get("FROM_EMAIL", "[email protected]")

def send_email(to, subject, html):
    """Send a single email. Returns True on success, False on failure."""
    try:
        resend.Emails.send({
            "from": FROM_EMAIL,
            "to": to,
            "subject": subject,
            "html": html
        })
        return True
    except Exception as e:
        print(f"Email error: {e}")
        return False

def send_welcome_email(user_email, user_name):
    """Send a welcome email to a new user."""
    html = render_template_string("""
        <h1>Welcome, {{ name }}!</h1>
        <p>Thanks for joining. Your account is ready.</p>
        <p>— The Team</p>
    """, name=user_name)

    return send_email(
        to=user_email,
        subject=f"Welcome, {user_name}!",
        html=html
    )

Key design decisions:

  • send_email() is generic — accepts any HTML string
  • Specific helpers like send_welcome_email() build on top of it
  • Returns True/False so routes can handle failures without crashing
  • The API key is loaded once at module import time

Calling It From a Flask Route

from utils.email import send_welcome_email

@app.route("/register", methods=["POST"])
def register():
    name = request.form.get("name")
    email = request.form.get("email")
    password = request.form.get("password")

    hashed = generate_password_hash(password)
    with get_db() as conn:
        with conn.cursor() as cur:
            cur.execute(
                "INSERT INTO users (name, email, password_hash) VALUES (%s, %s, %s)",
                (name, email, hashed)
            )
            conn.commit()

    # Send welcome email — failure does not crash the request
    send_welcome_email(email, name)

    return redirect("/dashboard")

The route stays thin. Email logic lives entirely in utils/email.py.

Richer HTML Email Template

For fully branded emails, define the HTML as a template string:

WELCOME_TEMPLATE = """
<!DOCTYPE html>
<html>
<body style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
    <h2 style="color: #333;">Welcome, {{ name }}!</h2>
    <p>Your account has been created successfully.</p>
    <a href="{{ app_url }}/dashboard"
       style="background: #333; color: white; padding: 12px 24px;
              text-decoration: none; border-radius: 4px; display: inline-block;">
        Go to Dashboard
    </a>
    <p style="color: #999; font-size: 12px; margin-top: 32px;">
        You received this because you signed up at {{ app_url }}.
    </p>
</body>
</html>
"""

def send_welcome_email(user_email, user_name):
    html = render_template_string(
        WELCOME_TEMPLATE,
        name=user_name,
        app_url=os.environ.get("APP_URL", "https://yourapp.com")
    )
    return send_email(user_email, f"Welcome, {user_name}!", html)

Skip Real Sends in Development

During development, log the email instead of sending it:

def send_email(to, subject, html):
    if os.environ.get("FLASK_ENV") == "development":
        print(f"[DEV EMAIL] To: {to} | Subject: {subject}")
        return True
    try:
        resend.Emails.send({
            "from": FROM_EMAIL,
            "to": to,
            "subject": subject,
            "html": html
        })
        return True
    except Exception as e:
        print(f"Email error: {e}")
        return False

Add FLASK_ENV=development to your .env to skip real sends locally.

AI Prompt That Generated This

"Create utils/email.py for a Flask app using the Resend Python SDK. Include a generic send_email(to, subject, html) function that returns True/False and a send_welcome_email(email, name) helper. Load RESEND_API_KEY from os.environ. Handle exceptions without crashing the caller."

Next Steps

Was this helpful?

Get More Flask Vibe Tutorials

Join 1,000+ developers getting weekly Flask tips and AI-friendly code patterns.

No spam. Unsubscribe anytime.