> ## Documentation Index
> Fetch the complete documentation index at: https://ahasend.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> AhaSend stores all message data in the EU. See /facts.
> The API base URL is https://api.ahasend.com. Send the API key in the Authorization: Bearer header. See /api-reference/authentication.
> Use send.ahasend.com for SMTP in the EU, or send-us.ahasend.com as a US connection point forwarding to the EU. Ports 25, 587 and 2525 use STARTTLS. Port 465 is not supported. See /smtp.
> For send-only integrations, create an API Key v2 under Credentials → Add and scope it to messages:send:{your-domain}. See /send-api/credentials and /api-reference/scopes.

# How to Send Email with Flask via SMTP

> Send email from Flask with AhaSend SMTP and Python smtplib: test in sandbox mode, require STARTTLS and add a server-only command with failure handling.

AhaSend lets Flask send transactional email over SMTP; this guide uses Python's built-in smtplib and a Flask command to keep sending on the server.

Before sending, [verify your domain](/docs/domains) and [create an SMTP credential](/docs/smtp/credentials). Use a [sandbox credential](/docs/send-api/sandbox) for these examples so no email is delivered. Keep the username and password in your server's secret store.

Use Python 3.13. Set `SMTP_USERNAME`, `SMTP_PASSWORD` and `AHASEND_FROM` in your shell or secret store. A separate Flask mail extension is not needed.

## Install Flask

```bash theme={null}
python3.13 -m venv .venv
source .venv/bin/activate
python -m pip install 'Flask>=3.1,<4'
```

Save this as `quick_send.py`:

```python quick_send.py theme={null}
import os, smtplib, ssl
from email.message import EmailMessage
from flask import Flask
app = Flask(__name__)
with app.app_context():
    message = EmailMessage()
    message["From"] = os.environ["AHASEND_FROM"]
    message["To"] = "recipient@example.com"
    message["Subject"] = "Flask sandbox test"
    message.set_content("Hello from AhaSend")
    with smtplib.SMTP("send.ahasend.com", 587, timeout=10) as smtp:
        smtp.starttls(context=ssl.create_default_context())
        smtp.login(os.environ["SMTP_USERNAME"], os.environ["SMTP_PASSWORD"])
        if smtp.send_message(message):
            raise RuntimeError("A recipient was rejected")
    print("Accepted by SMTP")
```

Run `python quick_send.py`. A failure raises an exception and exits with an error.

## Add a Flask Command for Your Worker

Save this as `app.py`. Set `AHASEND_TO=recipient@example.com` for testing. The command has no public HTTP endpoint. Flask's [command support](https://flask.palletsprojects.com/en/stable/cli/#custom-commands) runs it with the application context active.

```python app.py theme={null}
import os, smtplib, ssl
from email.message import EmailMessage
import click
from flask import Flask
app = Flask(__name__)

@app.cli.command("send-welcome")
def send_welcome():
    try:
        names = ("SMTP_USERNAME", "SMTP_PASSWORD", "AHASEND_FROM", "AHASEND_TO")
        if any(not os.environ.get(name) for name in names):
            raise ValueError("Missing configuration")
        message = EmailMessage()
        message["From"] = os.environ["AHASEND_FROM"]
        message["To"] = os.environ["AHASEND_TO"]
        message["Subject"] = "Your account is ready"
        message.set_content("Welcome to your account.")
        message.add_alternative("<p>Welcome to your account.</p>", subtype="html")
        with smtplib.SMTP("send.ahasend.com", 587, timeout=10) as smtp:
            smtp.starttls(context=ssl.create_default_context())
            smtp.login(os.environ["SMTP_USERNAME"], os.environ["SMTP_PASSWORD"])
            if smtp.send_message(message):
                raise RuntimeError("A recipient was rejected")
        click.echo("Accepted by SMTP")
    except (OSError, smtplib.SMTPException, ValueError, RuntimeError):
        raise click.ClickException("Email failed; check the job and SMTP settings.") from None
```

Run `flask --app app send-welcome`. SMTP acceptance is not inbox delivery; use [delivery webhooks](/docs/integrations/webhooks) to track the outcome. When moving this into a queue worker, read the recipient from an authorized job, save the job result, and review uncertain submissions before retrying. SMTP has no API idempotency key.

## Related Guides

* Before sending: [verify your domain](/docs/domains) and [create an SMTP credential](/docs/smtp/credentials).
* Connection and message rules: [SMTP hosts, ports and limits](/docs/smtp), [special headers](/docs/smtp/special-headers), [sandbox mode](/docs/send-api/sandbox), [retention](/docs/retention) and [plan features](/docs/facts#plans-and-features).
* Other ways to send: [REST API](/docs/send-api/send-email), [AhaSend CLI](/docs/cli/quickstart), [Node.js SDK](/docs/guides/nodejs-sdk) and [Go SDK](/docs/guides/go-sdk).
* Delivery events: [webhook setup](/docs/integrations/webhooks) and [local webhook testing](/docs/cli/webhook-testing).
