Installation and configuration

Installing Anymail

It’s easiest to install Anymail from PyPI using pip.

$ pip install django-anymail[sendgrid,sparkpost]

The [sendgrid,sparkpost] part of that command tells pip you also want to install additional packages required for those ESPs. You can give one or more comma-separated, lowercase ESP names. (Most ESPs don’t have additional requirements, so you can often just skip this. Or change your mind later. Anymail will let you know if there are any missing dependencies when you try to use it.)

Configuring Django’s email backend

To use Anymail for sending email, edit your Django project’s settings.py:

  1. Add anymail to your INSTALLED_APPS (anywhere in the list):

    INSTALLED_APPS = [
        # ...
        "anymail",
        # ...
    ]
    
  2. Add an ANYMAIL settings dict, substituting the appropriate settings for your ESP:

    ANYMAIL = {
        "MAILGUN_API_KEY": "<your Mailgun key>",
    }
    

    The exact settings vary by ESP. See the supported ESPs section for specifics.

  3. Change your existing Django EMAIL_BACKEND to the Anymail backend for your ESP. For example, to send using Mailgun by default:

    EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
    

    (EMAIL_BACKEND sets Django’s default for sending emails; you can also use multiple Anymail backends to send particular messages through different ESPs.)

Finally, if you don’t already have a DEFAULT_FROM_EMAIL in your settings, this is a good time to add one. (Django’s default is “webmaster@localhost”, which some ESPs will reject.)

With the settings above, you are ready to send outgoing email through your ESP. If you also want to enable status tracking, continue with the optional settings below. Otherwise, skip ahead to Sending email.

Configuring status tracking webhooks (optional)

Anymail can optionally connect to your ESP’s event webhooks to notify your app of status like bounced and rejected emails, successful delivery, message opens and clicks, and other tracking.

If you aren’t using Anymail’s webhooks, skip this section.

Warning

Webhooks are ordinary urls, and are wide open to the internet. You must use care to avoid creating security vulnerabilities that could expose your users’ emails and other private information, or subject your app to malicious input data.

At a minimum, your site should use SSL (https), and you should configure webhook authorization as described below.

See Securing webhooks for additional information.

If you want to use Anymail’s status tracking webhooks, follow the steps above to configure an Anymail backend, and then:

  1. In your settings.py, add WEBHOOK_AUTHORIZATION to the ANYMAIL block:

    ANYMAIL = {
        ...
        'WEBHOOK_AUTHORIZATION': '<a random string>:<another random string>',
    }
    

    This setting should be a string with two sequences of random characters, separated by a colon. It is used as a shared secret, known only to your ESP and your Django app, to ensure nobody else can call your webhooks.

    We suggest using 16 characters (or more) for each half of the secret. Always generate a new, random secret just for this purpose. (Don’t use your Django secret key or ESP’s API key.)

    An easy way to generate a random secret is to run this command in a shell:

    $ python -c "from django.utils import crypto; print(':'.join(crypto.get_random_string(16) for _ in range(2)))"
    

    (This setting is actually an HTTP basic auth string. You can also set it to a list of auth strings, to simplify credential rotation or use different auth with different ESPs. See ANYMAIL_WEBHOOK_AUTHORIZATION in the Securing webhooks docs for more details.)

  2. In your project’s urls.py, add routing for the Anymail webhook urls:

    from django.conf.urls import include, url
    
    urlpatterns = [
        ...
        url(r'^anymail/', include('anymail.urls')),
    ]
    

    (You can change the “anymail” prefix in the first parameter to url() if you’d like the webhooks to be served at some other URL. Just match whatever you use in the webhook URL you give your ESP in the next step.)

  3. Enter the webhook URL(s) into your ESP’s dashboard or control panel. In most cases, the URL will be:

    https://random:random@yoursite.example.com/anymail/esp/tracking/

    • “https” (rather than http) is strongly recommended
    • random:random is the WEBHOOK_AUTHORIZATION string you created in step 1
    • yoursite.example.com is your Django site
    • “anymail” is the url prefix (from step 2)
    • esp is the lowercase name of your ESP (e.g., “sendgrid” or “mailgun”)
    • “tracking” is used for Anymail’s sent-mail event tracking webhooks

    Some ESPs support different webhooks for different tracking events. You can usually enter the same Anymail webhook URL for all of them (or all that you want to receive). But be sure to check the specific details for your ESP under Supported ESPs.

    Also, some ESPs try to validate the webhook URL immediately when you enter it. If so, you’ll need to deploy your Django project to your live server before you can complete this step.

    Some WSGI servers may need additional settings to pass HTTP authorization headers through to Django. For example, Apache with mod_wsgi requires WSGIPassAuthorization On, else Anymail will complain about “missing or invalid basic auth” when your webhook is called.

See Tracking sent mail status for information on creating signal handlers and the status tracking events you can receive.

Anymail settings reference

You can add Anymail settings to your project’s settings.py either as a single ANYMAIL dict, or by breaking out individual settings prefixed with ANYMAIL_. So this settings dict:

ANYMAIL = {
    "MAILGUN_API_KEY": "12345",
    "SEND_DEFAULTS": {
        "tags": ["myapp"]
    },
}

...is equivalent to these individual settings:

ANYMAIL_MAILGUN_API_KEY = "12345"
ANYMAIL_SEND_DEFAULTS = {"tags": ["myapp"]}

In addition, for some ESP settings like API keys, Anymail will look for a setting without the ANYMAIL_ prefix if it can’t find the Anymail one. (This can be helpful if you are using other Django apps that work with the same ESP.)

MAILGUN_API_KEY = "12345"  # used only if neither ANYMAIL["MAILGUN_API_KEY"]
                           # nor ANYMAIL_MAILGUN_API_KEY have been set

Finally, for complex use cases, you can override most settings on a per-instance basis by providing keyword args where the instance is initialized (e.g., in a get_connection() call to create an email backend instance, or in View.as_view() call to set up webhooks in a custom urls.py). To get the kwargs parameter for a setting, drop “ANYMAIL” and the ESP name, and lowercase the rest: e.g., you can override ANYMAIL_MAILGUN_API_KEY by passing api_key="abc" to get_connection(). See Mixing email backends for an example.

There are specific Anymail settings for each ESP (like API keys and urls). See the supported ESPs section for details. Here are the other settings Anymail supports:

IGNORE_RECIPIENT_STATUS

Set to True to disable AnymailRecipientsRefused exceptions on invalid or rejected recipients. (Default False.) See Refused recipients.

ANYMAIL = {
    ...
    "IGNORE_RECIPIENT_STATUS": True,
}

SEND_DEFAULTS and ESP_SEND_DEFAULTS`

A dict of default options to apply to all messages sent through Anymail. See Global send defaults.

IGNORE_UNSUPPORTED_FEATURES

Whether Anymail should raise AnymailUnsupportedFeature errors for email with features that can’t be accurately communicated to the ESP. Set to True to ignore these problems and send the email anyway. See Unsupported features. (Default False.)

WEBHOOK_AUTHORIZATION

A 'random:random' shared secret string. Anymail will reject incoming webhook calls from your ESP that don’t include this authorization. You can also give a list of shared secret strings, and Anymail will allow ESP webhook calls that match any of them (to facilitate credential rotation). See Securing webhooks.

Default is unset, which leaves your webhooks insecure. Anymail will warn if you try to use webhooks with setting up authorization.

This is actually implemented using HTTP basic authorization, and the string is technically a “username:password” format. But you should not use any real username or password for this shared secret.