Skip to Content
FeaturesModulesEmailConfiguration

Email Configuration

EmailConfig is a Pydantic v2 model set on DjangoConfig.email. It maps directly to Django’s EMAIL_* settings and is applied automatically at startup.

from django_cfg.models.services.email import EmailConfig

EmailConfig Fields

Backend

FieldTypeDefaultDescription
backendstr"console"Email backend type
ValueDjango classUse case
"smtp"django.core.mail.backends.smtp.EmailBackendProduction SMTP
"gateway"django_cfg.core.backends.gateway.GatewayEmailBackendProduction via trusted HTTP gateway (no provider credentials in Django)
"console"django.core.mail.backends.console.EmailBackendPrint to stdout (development)
"file"django.core.mail.backends.filebased.EmailBackendWrite to files (testing)
"memory"django.core.mail.backends.locmem.EmailBackendStore in memory (unit tests)
"dummy"django.core.mail.backends.dummy.EmailBackendDiscard silently

SMTP Connection

FieldTypeDefaultDescription
hoststr"localhost"SMTP server hostname
portint587SMTP port (1–65535)
usernameOptional[str]NoneSMTP authentication username
passwordOptional[str]NoneSMTP authentication password
use_tlsboolTrueUse STARTTLS upgrade
use_sslboolFalseUse SSL/TLS wrapper from the start
ssl_verifyboolTrueVerify SSL certificate
timeoutint30Connection timeout in seconds (1–300)

use_tls=True and use_ssl=True cannot both be set. The validator raises ValueError if both are enabled. Use use_tls=True for port 587 (STARTTLS) and use_ssl=True for port 465 (SSL).

When ssl_verify=False, a custom backend is used that skips certificate verification. Never use this in production.

From Address

FieldTypeDefaultDescription
default_fromstr"[email protected]"Default sender address (must contain @)

If DjangoConfig.project_name is set, the from address is automatically formatted as "Project Name <[email protected]>". You can override this by passing a pre-formatted string with < and >.

File Backend

FieldTypeDefaultDescription
file_pathstr"emails/"Directory for file backend output

Only used when backend="file".

HTTP Email Gateway

FieldTypeDefaultDescription
gateway_urlOptional[str]NoneGateway send endpoint (e.g. https://email-api.example.com/send)
gateway_secretOptional[str]NoneShared secret presented to the gateway as a Bearer token

Only used when backend="gateway". Both fields are required for that backend — the model validator raises ValueError if either is missing.

email = EmailConfig( backend="gateway", gateway_url="https://email-api.example.com/send", gateway_secret="${EMAIL_GATEWAY_SECRET}", default_from="[email protected]", )

GatewayEmailBackend POSTs each Django EmailMessage as one JSON request to gateway_url with Authorization: Bearer <gateway_secret>. The gateway owns the provider integration — which ESP is used, which sender identities are allowed, what rate limits apply. The Django app never holds provider credentials and never knows which provider sits behind the gateway. A typical gateway is a Cloudflare Worker with an Email Sending binding.

Wire contract (gateway payload v1) — deliberately mirrors the Cloudflare Email Sending REST shape so a thin Worker can forward it to a provider binding without re-modelling:

Payload fieldContent
to / cc / bccLists of address strings
from / reply_toString or {address, name} object (parsed from "Name <a@b>")
subjectMessage subject
text / htmlMessage bodies — at least one present (HTML alternatives are lifted into html)
attachments{content (base64), filename, type, disposition[, content_id]} — inline attachments with a Content-ID get disposition: "inline"
headersPassthrough of extra_headers

Response envelope expected from the gateway:

{"success": true, "errors": [], "result": {"delivered": [], "permanent_bounces": [], "queued": []}}

Behavior details:

  • Only transient statuses (429, 500, 502, 503, 504) and network errors are retried — up to 3 attempts with exponential backoff. 4xx validation/auth errors are permanent.
  • success: false raises GatewayEmailError (respects fail_silently).
  • permanent_bounces in a successful response are logged as warnings, not raised — the message was accepted, but some recipients are suppressed on the provider side.
  • timeout (the shared EmailConfig field) applies to the HTTP request; one httpx.Client is reused across a send_messages() batch.
FieldTypeDefaultDescription
subscribe_urlOptional[str]NoneNewsletter subscribe URL for the system-email P.S. footer

Works with any backend. When set, the shared system-email template (templates/emails/base_email.html) renders a footer line — P.S. Want {project_name} product updates? One click — subscribe — linking to this URL. DjangoEmailService injects subscribe_url into the template context automatically; unset means no footer.

DjangoConfig Fields

FieldTypeDefaultDescription
emailOptional[EmailConfig]NoneEmail configuration
admin_emailslist[str][]Admin addresses for send_admin_email()
from django_cfg import DjangoConfig from django_cfg.models.services.email import EmailConfig class MyConfig(DjangoConfig): email = EmailConfig( backend="smtp", host="smtp.gmail.com", port=587, username="${EMAIL_USER}", password="${EMAIL_PASSWORD}", use_tls=True, default_from="[email protected]", ) admin_emails = ["[email protected]", "[email protected]"]

Provider Examples

Gmail (App Password)

Gmail requires a 16-character App Password when 2-Step Verification is enabled. Create one at myaccount.google.com/apppasswords .

email = EmailConfig( backend="smtp", host="smtp.gmail.com", port=587, username="[email protected]", password="${GMAIL_APP_PASSWORD}", use_tls=True, default_from="[email protected]", )

SendGrid

email = EmailConfig( backend="smtp", host="smtp.sendgrid.net", port=587, username="apikey", password="${SENDGRID_API_KEY}", use_tls=True, default_from="[email protected]", )

Mailgun

email = EmailConfig( backend="smtp", host="smtp.mailgun.org", port=587, username="[email protected]", password="${MAILGUN_SMTP_PASSWORD}", use_tls=True, default_from="[email protected]", )

AWS SES (SMTP Interface)

email = EmailConfig( backend="smtp", host="email-smtp.us-east-1.amazonaws.com", port=587, username="${AWS_SES_SMTP_USER}", password="${AWS_SES_SMTP_PASSWORD}", use_tls=True, default_from="[email protected]", )

Development (Console Output)

email = EmailConfig(backend="console")

Development (File Inspection)

email = EmailConfig(backend="file", file_path="sent_emails/")

Django Settings Mapping

EmailConfig maps to standard Django settings applied automatically at startup:

EmailConfig fieldDjango setting
backendEMAIL_BACKEND
hostEMAIL_HOST
portEMAIL_PORT
use_tlsEMAIL_USE_TLS
use_sslEMAIL_USE_SSL
timeoutEMAIL_TIMEOUT
usernameEMAIL_HOST_USER
passwordEMAIL_HOST_PASSWORD
default_fromDEFAULT_FROM_EMAIL
gateway_urlEMAIL_GATEWAY_URL (gateway backend only)
gateway_secretEMAIL_GATEWAY_SECRET (gateway backend only)
subscribe_urlEMAIL_SUBSCRIBE_URL (only when set)

You do not call to_django_settings() directly — django-cfg applies it during configuration loading.

See Also

Last updated on