Pre- and post-send signals

Anymail provides pre-send and post-send signals you can connect to trigger actions whenever messages are sent through an Anymail backend.

Be sure to read Django’s listening to signals docs for information on defining and connecting signal receivers.

Pre-send signal

You can use Anymail’s pre_send signal to examine or modify messages before they are sent. For example, you could implement your own email suppression list:

from anymail.exceptions import AnymailCancelSend
from anymail.signals import pre_send
from django.dispatch import receiver
from email.utils import parseaddr

from your_app.models import EmailBlockList

@receiver(pre_send)
def filter_blocked_recipients(sender, message, **kwargs):
    # Cancel the entire send if the from_email is blocked:
    if not ok_to_send(message.from_email):
        raise AnymailCancelSend("Blocked from_email")
    # Otherwise filter the recipients before sending:
    message.to = [addr for addr in message.to if ok_to_send(addr)]
    message.cc = [addr for addr in message.cc if ok_to_send(addr)]

def ok_to_send(addr):
    # This assumes you've implemented an EmailBlockList model
    # that holds emails you want to reject...
    name, email = parseaddr(addr)  # just want the <email> part
    try:
        EmailBlockList.objects.get(email=email)
        return False  # in the blocklist, so *not* OK to send
    except EmailBlockList.DoesNotExist:
        return True  # *not* in the blocklist, so OK to send

Any changes you make to the message in your pre-send signal receiver will be reflected in the ESP send API call, as shown for the filtered “to” and “cc” lists above. Note that this will modify the original EmailMessage (not a copy)—be sure this won’t confuse your sending code that created the message.

If you want to cancel the message altogether, your pre-send receiver function can raise an AnymailCancelSend exception, as shown for the “from_email” above. This will silently cancel the send without raising any other errors.

anymail.signals.pre_send

Signal delivered before each EmailMessage is sent.

Your pre_send receiver must be a function with this signature:

def my_pre_send_handler(sender, message, esp_name, **kwargs):

(You can name it anything you want.)

Parameters:
  • sender (class) – The Anymail backend class processing the message. This parameter is required by Django’s signal mechanism, and despite the name has nothing to do with the email message’s sender. (You generally won’t need to examine this parameter.)
  • message (EmailMessage) – The message being sent. If your receiver modifies the message, those changes will be reflected in the ESP send call.
  • esp_name (str) – The name of the ESP backend in use (e.g., “SendGrid” or “Mailgun”).
  • **kwargs – Required by Django’s signal mechanism (to support future extensions).
Raises:

anymail.exceptions.AnymailCancelSend if your receiver wants to cancel this message without causing errors or interrupting a batch send.

Post-send signal

You can use Anymail’s post_send signal to examine messages after they are sent. This is useful to centralize handling of the sent status for all messages.

For example, you could implement your own ESP logging dashboard (perhaps combined with Anymail’s event-tracking webhooks):

from anymail.signals import post_send
from django.dispatch import receiver

from your_app.models import SentMessage

@receiver(post_send)
def log_sent_message(sender, message, status, esp_name, **kwargs):
    # This assumes you've implemented a SentMessage model for tracking sends.
    # status.recipients is a dict of email: status for each recipient
    for email, recipient_status in status.recipients.items():
        SentMessage.objects.create(
            esp=esp_name,
            message_id=recipient_status.message_id,  # might be None if send failed
            email=email,
            subject=message.subject,
            status=recipient_status.status,  # 'sent' or 'rejected' or ...
        )
anymail.signals.post_send

Signal delivered after each EmailMessage is sent.

If you register multiple post-send receivers, Anymail will ensure that all of them are called, even if one raises an error.

Your post_send receiver must be a function with this signature:

def my_post_send_handler(sender, message, status, esp_name, **kwargs):

(You can name it anything you want.)

Parameters:
  • sender (class) – The Anymail backend class processing the message. This parameter is required by Django’s signal mechanism, and despite the name has nothing to do with the email message’s sender. (You generally won’t need to examine this parameter.)
  • message (EmailMessage) – The message that was sent. You should not modify this in a post-send receiver.
  • status (AnymailStatus) – The normalized response from the ESP send call. (Also available as message.anymail_status.)
  • esp_name (str) – The name of the ESP backend in use (e.g., “SendGrid” or “Mailgun”).
  • **kwargs – Required by Django’s signal mechanism (to support future extensions).