Django Postbox

Capture and forward

Postbox is a regular Django email backend. For every message your project sends, it does two things:

  1. It keeps a copy for every recipient: To, Cc and Bcc. Each address gets its own inbox, so alice@example.com and bob@example.com each see the mail as they received it.
  2. It passes the message on, if you tell it where to. Otherwise the mail stops here.

Pick the recipe that matches your situation.

Keep everything on your machine #

This is the default. With no next backend, Postbox is the end of the line: you read the mail in the inbox, and nobody else ever gets it.

settings.py
MAILERS = {
    "default": {"BACKEND": "django_postbox.backends.EmailBackend"},
}

This is what you want on your laptop, and on review or staging servers where testers sign up with real-looking addresses.

Also print every mail in the terminal #

Handy when you work in the terminal anyway: the mail shows up in the inbox and in your runserver output.

settings.py
MAILERS = {
    "default": {
        "BACKEND": "django_postbox.backends.EmailBackend",
        "OPTIONS": {"forward_to": "console"},
    },
    "console": {
        "BACKEND": "django.core.mail.backends.console.EmailBackend",
    },
}

forward_to names another entry in MAILERS. POSTBOX_FORWARD_BACKEND is the dotted path of any email backend.

Deliver for real and keep a copy #

Forward to SMTP and Postbox becomes a logbook: everyone gets their mail as usual, and you can still look up what was sent.

settings.py
MAILERS = {
    "default": {
        "BACKEND": "django_postbox.backends.EmailBackend",
        "OPTIONS": {"forward_to": "smtp"},
    },
    "smtp": {
        "BACKEND": "django.core.mail.backends.smtp.EmailBackend",
        "OPTIONS": {"host": "smtp.example.com", "port": 587, "use_tls": True},
    },
}
Every message is forwarded

Postbox passes on every message, also the ones it captured. Never forward to a real SMTP server on a test server with real customer addresses in its database: they would get your test mail.

Keep some mail out of the inbox #

Some mail is noise in the inbox, like error reports to your own team. List those domains and Postbox will not keep a copy:

settings.py
POSTBOX_SKIP_DOMAINS = ["company.com"]

Subdomains count too, so eu.company.com is skipped as well. A mail to both dev@company.com and alice@example.com still lands in Alice’s inbox, only the copy for company.com is left out.

Note

Skipping only decides what Postbox keeps. Whether the mail goes anywhere is still up to the forward: with a forward the skipped mail is delivered, without one it is dropped.

When storing fails #

If Postbox cannot store a mail, for example because the database is down, you get the error, just like with any other backend. Sending with fail_silently=True ignores it, on Django versions that still support that argument.

What about a loop?

A forward that points back at Postbox would send every mail around forever. Postbox refuses to start instead, with an ImproperlyConfigured error that tells you so.