Complete the airflow survey & get a free airflow 3 certification!

SMTP Connection

The SMTP connection type enables integrations such as SmtpHook.

Note

The legacy helper in airflow.utils.email is scheduled for deprecation and will be removed in a future major release. Please migrate to SmtpHook or other provider-level utilities for sending emails.

Default Connection ID

The default ID is smtp_default when no conn_id is supplied.

Authenticating to SMTP

Two methods are supported:

  • Basic – traditional username + password.

  • OAuth 2 / XOAUTH2 – bearer-token based, required by Gmail API, Microsoft 365 / Outlook.com and other modern providers.

If you omit credentials the hook attempts an anonymous session, accepted only by open-relay test servers.

Configuring the Connection

Login

Username (for example user@example.com).

Password

Password or app-specific password. Ignored when auth_type="oauth2".

Host

SMTP server hostname (for example smtp.gmail.com).

Port

Port number. Defaults to 465 when SSL is enabled, otherwise 587.

Extra (optional – JSON)

Additional parameters.

General

  • from_email – Default From: address.

  • disable_ssl (bool) – Disable SSL/TLS entirely. Default false.

  • disable_tls (bool) – Skip STARTTLS. Default false.

  • timeout (int) – Socket timeout (seconds). Default 30.

  • retry_limit (int) – Connection attempts before raising. Default 5.

  • ssl_context"default" | "none" See SSL / TLS Notes.

Templating

  • subject_template – File path for custom subject.

  • html_content_template – File path for custom HTML body.

Authentication

  • auth_type"basic" (default) | "oauth2"

  • access_token – OAuth 2 bearer (one-hour).

  • client_id / client_secret – Credentials for token refresh. (auto-defaults to Google or Microsoft).

  • tenant_id – Azure tenant (default "common").

  • scope – OAuth scope

    • Gmail: https://mail.google.com/

    • Outlook (Graph): https://outlook.office.com/.default

SSL / TLS Notes

  • ssl_context="default" – reasonable trust store & secure ciphers (recommended)

  • ssl_context="none"disables certificate validation; use only for local testing with self-signed certificates.

Examples

Basic Auth — SendGrid (STARTTLS 587)

export AIRFLOW_CONN_SMTP_SENDGRID='smtp://apikey:SG.YOUR_API_KEY@smtp.sendgrid.net:587?\
disable_ssl=true&\
from_email=you%40example.com'

OAuth 2 — Gmail (access token, STARTTLS 587)

export AIRFLOW_CONN_SMTP_GMAIL='smtp://your.name%40gmail.com@smtp.gmail.com:587?\
auth_type=oauth2&\
access_token=ya29.<URL_ENCODED_TOKEN>&\
from_email=your.name%40gmail.com&\
disable_ssl=true'

OAuth 2 — Gmail (SSL 465)

export AIRFLOW_CONN_SMTP_GMAIL_SSL='smtp://your.name%40gmail.com@smtp.gmail.com:465?\
auth_type=oauth2&\
access_token=ya29.<URL_ENCODED_TOKEN>&\
from_email=your.name%40gmail.com&\
disable_tls=true'

OAuth 2 — Microsoft 365 (client credentials 587)

export AIRFLOW_CONN_SMTP_M365='smtp://user%40contoso.com@smtp.office365.com:587?\
auth_type=oauth2&\
client_id=YOUR_APP_ID&\
client_secret=YOUR_SECRET&\
tenant_id=YOUR_TENANT_ID&\
scope=https%3A%2F%2Foutlook.office.com%2F.default&\
disable_ssl=true'

OAuth2 — Microsoft 365 (client-credential flow)

export AIRFLOW_CONN_SMTP_M365='smtp://user@contoso.com@smtp.office365.com:587?\
auth_type=oauth2&\
client_id=YOUR_APP_ID&\
client_secret=YOUR_SECRET&\
tenant_id=YOUR_TENANT_ID&\
scope=https%3A%2F%2Foutlook.office.com%2F.default'

Troubleshooting

Error message

Likely cause

Fix

SSL WRONG_VERSION_NUMBER

Port 587 but the connection starts with SSL (no STARTTLS).

Add disable_ssl=true or switch to port 465.

STARTTLS required

Port 465 yet the hook still issues STARTTLS.

Add disable_tls=true or switch to port 587.

530 Authentication Required

Access-token expired or missing the https://mail.google.com/ scope.

Generate a fresh token.

550 From address not verified

Sender identity not verified at the provider or from_email mismatch.

Verify the sender / domain and ensure from_email exactly matches it.

Programmatic creation

from airflow.models.connection import Connection

conn = Connection(
    conn_id="smtp_gmail_token",
    conn_type="smtp",
    host="smtp.gmail.com",
    login="me@gmail.com",
    extra={"auth_type": "oauth2", "access_token": "ya29.a0AfB..."},
)
print(conn.test_connection())

URI encoding

When creating connections programmatically or via the CLI, ensure that

When fields contain special characters (/, @, : …), URL-encode them, for example via airflow.models.connection.Connection.get_uri().

CLI creation (Gmail OAuth 2)

Prefer environment variables for portability, but you can also create the connection via CLI:

airflow connections add smtp_gmail_oauth2 \
  --conn-type smtp \
  --conn-host smtp.gmail.com \
  --conn-port 587 \
  --conn-login '<YOUR_EMAIL>@gmail.com' \
  --conn-extra '{
    "from_email": "<YOUR_EMAIL>@gmail.com",
    "auth_type": "oauth2",
    "access_token": "<YOUR_OAUTH2_ACCESS_TOKEN>",
    "disable_ssl": "true"
  }'

Note

The [smtp] section in airflow.cfg is used by the core e-mail helper slated for deprecation. When you switch to SmtpHook and supply a smtp_conn_id, the hook’s connection settings take precedence and the global [smtp] options may be ignored.

Using SmtpHook in a Dag

 1from datetime import datetime
 2
 3from airflow import DAG
 4from airflow.operators.python import PythonOperator
 5from airflow.providers.smtp.hooks.smtp import SmtpHook
 6
 7
 8def gmail_oauth2_test():
 9    with SmtpHook(smtp_conn_id="smtp_gmail_oauth2") as hook:
10        hook.send_email_smtp(
11            to="recipient@example.com",
12            subject="[Airflow→Gmail] OAuth2 OK",
13            html_content="<h3>Gmail XOAUTH2 works 🎉</h3>",
14        )
15
16
17with DAG(
18    dag_id="test_gmail_oauth2",
19    start_date=datetime(2025, 7, 1),
20    schedule=None,
21    catchup=False,
22    tags=["example"],
23) as dag:
24    PythonOperator(
25        task_id="send_mail",
26        python_callable=gmail_oauth2_test,
27    )

Was this entry helpful?