Viesti Sign in

Send email from Django over SMTP

Django sends email through its SMTP backend. Django 6.1 configures it with the new MAILERS setting; Django 6.0 and earlier, including the 5.2 LTS, use the EMAIL_* settings. Both are shown here with STARTTLS on port 587.

Before you start

  1. Sign in and verify the domain you send from. Viesti accepts a message only when its From address is on a domain verified in your workspace and the envelope sender (MAIL FROM), which most libraries take from the From address, is on the same domain. DNS setup lists the records.
  2. Create an API key on the Credentials page of the dashboard. It is shown once. The key is the SMTP password; the username is apikey.
Setting Value
Host smtp.viesti.email
Port 587 with STARTTLS, or 465 with TLS from the first byte
Username apikey
Password Your API key: mr_ followed by 64 hex characters
Authentication PLAIN, offered only once the connection is encrypted

Set the environment

export VIESTI_API_KEY="mr_your_api_key"
export MAIL_FROM="Acme <[email protected]>"

Django 6.1: MAILERS

Add to settings.py:

import os

MAILERS = {
    "default": {
        "BACKEND": "django.core.mail.backends.smtp.EmailBackend",
        "OPTIONS": {
            "host": "smtp.viesti.email",
            "port": 587,
            "use_tls": True,
            "username": "apikey",
            "password": os.environ["VIESTI_API_KEY"],
            "timeout": 10,
        },
    },
}
DEFAULT_FROM_EMAIL = os.environ["MAIL_FROM"]
SERVER_EMAIL = DEFAULT_FROM_EMAIL

"use_tls" is STARTTLS, the right choice for 587. Django 6.1 deprecates EMAIL_BACKEND and the EMAIL_* connection settings in favor of MAILERS: they still work, but each one emits a RemovedInDjango70Warning asking you to migrate before Django 7.0.

Django 5.2 and 6.0: EMAIL_* settings

import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.viesti.email"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "apikey"
EMAIL_HOST_PASSWORD = os.environ["VIESTI_API_KEY"]
EMAIL_TIMEOUT = 10
DEFAULT_FROM_EMAIL = os.environ["MAIL_FROM"]
SERVER_EMAIL = DEFAULT_FROM_EMAIL

Set both sender settings. DEFAULT_FROM_EMAIL is used when you pass no sender, and SERVER_EMAIL sends error reports to ADMINS. Their defaults, webmaster@localhost and root@localhost, are not on a verified domain, so Viesti refuses them.

Send an email

from django.core.mail import send_mail

send_mail(
    "Your receipt",
    "Thanks for your order.",
    None,  # None sends from DEFAULT_FROM_EMAIL
    ["[email protected]"],
)

Test it

Django ships a command that sends a test message from DEFAULT_FROM_EMAIL with the default mailer:

python manage.py sendtestemail [email protected]

No output means Viesti accepted it; check Activity in the dashboard. Django's test runner swaps in the in-memory backend, so your test suite never sends through Viesti.

Port 465

Replace "use_tls": True with "use_ssl": True and set "port": 465; on Django 6.0 and earlier, set EMAIL_USE_SSL = True and EMAIL_PORT = 465 instead of EMAIL_USE_TLS. Never set both.

Common errors

Error Cause and fix
smtplib.SMTPNotSupportedError: SMTP AUTH extension not supported by server. "use_tls" or EMAIL_USE_TLS is missing, so Django tried to log in before encrypting the connection.
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number "use_ssl" or EMAIL_USE_SSL on port 587. Use TLS for 587, SSL for 465.
InvalidMailer: MAILERS['default']: The 'use_ssl' and 'use_tls' OPTIONS are incompatible. Set at most one of them to True. Both options are set (Django 6.1). On earlier versions the same mistake raises ValueError: EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive.
smtplib.SMTPDataError: (550, b'5.0.0 sending not permitted') The sender is not on a verified domain, often because DEFAULT_FROM_EMAIL or SERVER_EMAIL still has its default.
ValueError: Invalid address 'Acme Inc. <…>': period in 'phrase' Django 6.1 rejects a display name containing a period unless it is quoted: MAIL_FROM='"Acme Inc." <[email protected]>'.

Replies from Viesti

Whatever the library, its error includes the server's reply. These are the ones caused by your setup or account:

Reply Meaning
535 5.7.8 Authentication failed The API key is wrong, revoked or empty. After ten failed attempts from one client address within ten minutes, that address is refused for ten minutes with 454 Too many failed authentications; retry later.
525 5.7.13 Workspace suspended; contact support The key is valid, but the workspace is suspended or under review. Write to [email protected]; a new key does not help.
550 5.0.0 sending not permitted The From domain is not verified in this workspace, the envelope sender is on a different domain than the From header, or the workspace is not active.
550 5.0.0 recipient suppressed The recipient is on your suppression list after a bounce or complaint; see Bounces.
451 4.0.0 submission quota exceeded The free plan's 75 emails per UTC day or 2,000 per UTC month are used up; see Limits. It is temporary: retry after the reset or upgrade.
452 4.5.3 Maximum limit of 50 recipients reached One message can have at most 50 recipients. Split the send.
552 5.0.0 message too large The message, attachments included, is over 10 MB.
554 5.0.0 rejected by content audit Content screening found the message in violation of the Terms of Service. It is not relayed.

A 250 reply means Viesti accepted the message. Content screening runs before relay; mail it flags is held for review and shows as quarantined on the Activity page.

Next steps

  • DNS setup: DKIM, SPF, DMARC and return-path records for your domain.
  • SMTP reference: ports, TLS and authentication in detail.
  • HTTP API: send the same message as JSON over HTTPS.
  • Limits and Bounces: quotas, message size and suppressions.

Settings are from the Django 6.1 Sending email documentation; tested with Django 6.1.1 and 5.2.17. Checked 26 September 2026.