### Install Dependencies and Build Docs Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Install development requirements and build the documentation using tox. ```console python -m pip install -r requirements-dev.txt tox -e docs # build the docs using Sphinx ``` -------------------------------- ### Install Anymail with Mailgun Support Source: https://github.com/anymail/django-anymail/blob/main/README.rst Install Anymail using pip, including specific packages for the Mailgun ESP. The bracketed part installs ESP-specific dependencies. ```console $ pip install "django-anymail[mailgun]" ``` -------------------------------- ### Install Development Requirements with Pip Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Install tox and other development requirements before running tests locally. ```console python -m pip install -r requirements-dev.txt ``` -------------------------------- ### Serve Built Documentation Locally Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Start a local HTTP server to view the built documentation in a browser. ```console (cd .tox/docs/_html; python -m http.server 8123 --bind 127.0.0.1) ``` -------------------------------- ### Install Anymail with SendGrid Extras Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sendgrid.md To optionally use Anymail's SendGrid webhook signature verification, install Anymail with the '[sendgrid]' extra. Alternatively, install cryptography separately. ```console $ python -m pip install 'django-anymail[sendgrid]' ``` -------------------------------- ### Install Django Anymail with Amazon SES Extra Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/amazon_ses.md Install Django Anymail with the amazon-ses extra to include the boto3 dependency. ```console $ pip install "django-anymail[amazon-ses]" ``` -------------------------------- ### Install Django Anymail with ESP Packages Source: https://github.com/anymail/django-anymail/blob/main/docs/installation.md Install the django-anymail package using pip. Include optional packages for specific ESPs like Amazon SES or Mailtrap in square brackets. ```console $ pip install "django-anymail[amazon-ses,mailtrap]" ``` -------------------------------- ### Load API Key from Environment Variable Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sparkpost.md Example of loading the SparkPost API key from an environment variable into Anymail settings. ```python "SPARKPOST_API_KEY": os.environ["SPARKPOST_API_KEY"] ``` -------------------------------- ### Install Testing Requirements Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Install the necessary requirements for running tests directly within your Python environment. ```console python -m pip install -r tests/requirements.txt ``` -------------------------------- ### Install Anymail with Resend Support Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/resend.md Install Anymail with the necessary package for Resend webhook signature validation. This is optional if you don't use status tracking webhooks with Resend. ```console python -m pip install 'django-anymail[resend]' ``` -------------------------------- ### Send using Inline Template with Merge Data Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/unisender_go.md This example demonstrates sending an email using an inline template defined directly in the code. Provide subject, body, and merge data for substitutions. Note that Unisender Go requires substitutions without whitespace in braces. ```python message = EmailMessage( from_email="shipping@example.com", to=["alice@example.com", "Bob "], # Use {{substitution}} variables in subject and body: subject="Your order {{order_no}} has shipped", body="""Hi {{name}}, We shipped your order {{order_no}} on {{ship_date}}. """, ) # (You'd probably also want to add an HTML body here.) # The substitution data is exactly the same as in the previous example: message.merge_data = { "alice@example.com": {"name": "Alice", "order_no": "12345"}, "bob@example.com": {"name": "Bob", "order_no": "54321"}, } message.merge_global_data = { "ship_date": "May 15", } message.send() ``` -------------------------------- ### Mailgun On-the-Fly Template with Recipient Variables Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/mailgun.md Example of an on-the-fly template using Mailgun's %recipient.variable% syntax for subject and body. Use this when defining templates directly in your Django code. ```python message = EmailMessage( from_email="shipping@example.com", # Use %recipient.___% syntax in subject and body: subject="Your order %recipient.order_no% has shipped", body="""Hi %recipient.name%, We shipped your order %recipient.order_no% on %recipient.ship_date%. """, to=["alice@example.com", "Bob "] ) # (you'd probably also set a similar html body with %recipient.___% variables) message.merge_data = { 'alice@example.com': {'name': "Alice", 'order_no": "12345"}, 'bob@example.com': {'name': "Bob", 'order_no": "54321"}, } message.merge_global_data = { 'ship_date': "May 15" # Anymail maps globals to all recipients } ``` -------------------------------- ### Use Scaleway esp_extra for Send Before Option Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/scaleway.md Extend Scaleway's Send an email API functionality by setting message-specific esp_extra parameters. This example shows how to use the 'send_before' option. ```python message.esp_extra = { # merged into send params: "send_before": "2025-08-13T02:22:00Z", } ``` -------------------------------- ### Sending Postmark Templated Email with Merge Data Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/postmark.md Use Anymail's merge_data and merge_global_data attributes to supply Postmark template variables. This example demonstrates setting per-recipient merge data for personalized emails. ```python message = EmailMessage( # (subject and body come from the template, so don't include those) to=["alice@example.com", "Bob "] ) message.template_id = 80801 # Postmark template id or alias message.merge_data = { 'alice@example.com': {'name': "Alice", 'order_no": "12345"}, 'bob@example.com': {'name': "Bob", 'order_no": "54321"}, } message.merge_global_data = { 'ship_date': "May 15", } ``` -------------------------------- ### Mailgun Stored Template with Template Substitutions Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/mailgun.md Example using a stored Mailgun template identified by 'template_id'. This approach uses {{ variable }} syntax within the Mailgun template itself. The substitution data structure remains the same as for on-the-fly templates. ```python message = EmailMessage( from_email="shipping@example.com", # The message body and html_body come from from the stored template. # (You can still use %recipient.___% fields in the subject:) subject="Your order %recipient.order_no% has shipped", to=["alice@example.com", "Bob "] ) message.template_id = 'shipping-notification' # name of template in our account # The substitution data is exactly the same as in the previous example: message.merge_data = { 'alice@example.com': {'name': "Alice", 'order_no": "12345"}, 'bob@example.com': {'name': "Bob", 'order_no": "54321"}, } message.merge_global_data = { 'ship_date': "May 15" # Anymail maps globals to all recipients } ``` -------------------------------- ### Using AnymailMessage for ESP Features Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/anymail_additions.md Demonstrates how to create an AnymailMessage instance and set ESP-specific attributes like tags and metadata directly in the constructor or after instantiation. Shows how to access sending status and recipient details after sending. ```python from anymail.message import AnymailMessage message = AnymailMessage( subject="Welcome", body="Welcome to our site", to=["New User "], tags=["Onboarding"], # Anymail extra in constructor ) # Anymail extra attributes: message.metadata = {"onboarding_experiment": "variation 1"} message.track_clicks = True message.send() status = message.anymail_status # available after sending status.message_id # e.g., '<12345.67890@example.com>' status.recipients["user1@example.com"].status # e.g., 'queued' ``` -------------------------------- ### Configure Anymail with individual settings Source: https://github.com/anymail/django-anymail/blob/main/docs/installation.md Alternatively, use individual settings prefixed with ANYMAIL_ for more granular control. ```python ANYMAIL_MAILGUN_API_KEY = "12345" ANYMAIL_SEND_DEFAULTS = {"tags": ["myapp"]} ``` -------------------------------- ### IAM Error Example for SendEmail Operation Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/amazon_ses.md This example illustrates a common IAM error message when permissions are insufficient for the 'ses:SendRawEmail' action, even when calling the SES v2 SendEmail API operation. ```text An error occurred (AccessDeniedException) when calling the **SendEmail** operation: User 'arn:...' is not authorized to perform **'ses:SendRawEmail'** on resource 'arn:...' ``` -------------------------------- ### Configuring Alternative Anymail Backend Settings Source: https://github.com/anymail/django-anymail/blob/main/docs/tips/multiple_backends.md Shows how to override default Anymail backend settings, such as API keys, when creating a connection. This allows for using different accounts or configurations for specific email sending tasks, like a separate Mailgun account for marketing emails. ```python # You can override settings.py settings with kwargs to get_connection. # This example supplies credentials for a different Mailgun sub-acccount: alt_mailgun_backend = get_connection('anymail.backends.mailgun.EmailBackend', api_key=MAILGUN_API_KEY_FOR_MARKETING) send_mail("Here's that info", "you wanted", "info@marketing.example.com", ["prospect@example.org"], connection=alt_mailgun_backend) ``` -------------------------------- ### Configure Anymail for Postmark Open Tracking Defaults Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/postmark.md Use Anymail's global send defaults to enable open tracking for most messages when using Postmark. Individual messages can then use track_opens = False to override this. ```python ANYMAIL = { # ... "SEND_DEFAULTS": { "track_opens": True }, } ``` -------------------------------- ### Message Tags Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/anymail_additions.md Apply a list of string tags to a message for segmentation and status tracking. For portability, use strings starting with alphanumeric characters. ```python message.tags = ["Order Confirmation", "Test Variant A"] ``` -------------------------------- ### Global Merge Data Example Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/templates.md Define template merge data that applies to all recipients in a batch send. This is useful for default values or information common to all emails. ```python message.merge_global_data = { 'PARTNER': "Acme, Inc.", 'OFFER': "5% off any Acme product", # a default OFFER } ``` -------------------------------- ### Accessing Email Headers Source: https://github.com/anymail/django-anymail/blob/main/docs/inbound.md Demonstrates how to access email headers using dictionary-like access and the get_all method for headers that can appear multiple times. ```python message['reply-to'] # the Reply-To header (header keys are case-insensitive) message.get_all('DKIM-Signature') # list of all DKIM-Signature headers ``` -------------------------------- ### Log Sent Messages with post_send Signal Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/signals.md Use the post_send signal to examine messages after they are sent. This example logs details of each sent recipient to a SentMessage model. ```python 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 ... ) ``` -------------------------------- ### Run All Tox Environments in Parallel Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Execute all available tox environments, leveraging parallel processing if possible. ```console tox --parallel auto ``` -------------------------------- ### Attaching Inline Image File to Email Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/anymail_additions.md Use attach_inline_image_file to embed an image in an email and get its Content-ID. Prefix the ID with 'cid:' in your HTML for the image source. ```python from django.core.mail import EmailMultiAlternatives from anymail.message import attach_inline_image_file message = EmailMultiAlternatives( ... ) cid = attach_inline_image_file(message, 'path/to/picture.jpg') html = '... Picture ...' % cid message.attach_alternative(html, 'text/html') message.send() ``` -------------------------------- ### Configure Django Settings for Anymail Source: https://github.com/anymail/django-anymail/blob/main/docs/quickstart.md Add 'anymail' to INSTALLED_APPS and configure ANYMAIL settings with your ESP's API key and domain. Set the EMAIL_BACKEND to Anymail's backend for your ESP. ```python INSTALLED_APPS = [ # ... "anymail", # ... ] ANYMAIL = { # (exact settings here depend on your ESP...) "MAILGUN_API_KEY": "", "MAILGUN_SENDER_DOMAIN": 'mg.example.com', # your Mailgun domain, if needed } EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" # or amazon_ses.EmailBackend, or... DEFAULT_FROM_EMAIL = "you@example.com" # if you don't already have this in settings SERVER_EMAIL = "your-server@example.com" # ditto (default from-email for Django errors) ``` -------------------------------- ### Get ESP Message ID from AnymailStatus Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/anymail_additions.md Access the message ID assigned by the ESP after a successful send. This ID is available via the anymail_status attribute on the message. ```python message.anymail_status.message_id ``` -------------------------------- ### Configure SparkPost API URL for EU Region Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sparkpost.md Specify the SparkPost API endpoint, for example, to use the EU region. Ensure the full, versioned endpoint is provided. ```python ANYMAIL = { ... "SPARKPOST_API_URL": "https://api.eu.sparkpost.com/api/v1", # use SparkPost EU } ``` -------------------------------- ### Configure SendGrid Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sendgrid.md Set the EMAIL_BACKEND in your settings.py to use Anymail's SendGrid backend. ```python EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend" ``` -------------------------------- ### Configure Anymail Settings for ESP Source: https://github.com/anymail/django-anymail/blob/main/docs/installation.md Set up the ANYMAIL dictionary in your Django settings.py, providing the necessary credentials for your chosen ESP, such as Mailgun API key. ```python ANYMAIL = { "MAILGUN_API_KEY": "", } ``` -------------------------------- ### Send Email with SES Message Tag Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/amazon_ses.md Example of sending an email with a single tag, which will be published as an SES Message Tag named 'Type' with the value 'Marketing' when AMAZON_SES_MESSAGE_TAG_NAME is configured. ```python message = EmailMessage(...) message.tags = ["Marketing"] message.send() ``` -------------------------------- ### Configure Unisender Go API Credentials Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/unisender_go.md Set your Unisender Go API key and the correct API URL for your account in the ANYMAIL setting. Ensure you select the appropriate URL based on your account's data center. ```python ANYMAIL = { "UNISENDER_GO_API_KEY": "", "UNISENDER_GO_API_URL": "https://go1.unisender.ru/ru/transactional/api/v1/", "UNISENDER_GO_API_URL": "https://go2.unisender.ru/ru/transactional/api/v1/" } ``` -------------------------------- ### Per-Recipient Merge Data Example Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/templates.md Define per-recipient merge data for template substitutions. Each key is an email address, and its value is a dictionary of merge field names and values for that specific recipient. ```python message.merge_data = { 'wile@example.com': {'NAME': "Wile E.", 'OFFER': "15% off anvils"}, 'rr@example.com': {'NAME': "Mr. Runner", 'OFFER': "instant tunnel paint"}, } ``` -------------------------------- ### Filter Blocked Recipients with pre_send Signal Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/signals.md Use the pre_send signal to examine or modify messages before sending. This example filters out recipients from a blocklist and can cancel the entire send if the sender is blocked. ```python 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 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 ``` -------------------------------- ### Configure Anymail in Django Settings Source: https://github.com/anymail/django-anymail/blob/main/README.rst Add 'anymail' to your INSTALLED_APPS and configure Anymail settings in settings.py. The exact settings depend on your chosen ESP. ```python INSTALLED_APPS = [ # ... "anymail", # ... ] ANYMAIL = { # (exact settings here depend on your ESP...) "MAILGUN_API_KEY": "", ``` -------------------------------- ### Simulate Anymail Inbound Events in Tests Source: https://github.com/anymail/django-anymail/blob/main/docs/tips/testing.md This snippet demonstrates how to simulate Anymail inbound events, including constructing an AnymailInboundMessage, and dispatching it to your inbound signal receivers. It requires importing AnymailInboundMessage, AnymailInboundEvent, and the inbound signal. ```python from anymail.inbound import AnymailInboundMessage from anymail.signals import AnymailInboundEvent, inbound from django.test import TestCase class EmailReceivingTests(TestCase): def test_inbound_event(self): # Build a simple AnymailInboundMessage and AnymailInboundEvent # (see tips for more complex messages after the example): message = AnymailInboundMessage.construct( from_email="user@example.com", to="comments@example.net", subject="subject", text="text body", html="html body") event = AnymailInboundEvent(message=message) # Invoke all registered Anymail inbound signal receivers: inbound.send(sender=object(), event=event, esp_name="TestESP") # Verify expected behavior of your receiver. What to test here # depends on how your code handles the inbound message. E.g., if # you create a user comment from the message, you might check: from myapp.models import MyCommentModel comment = MyCommentModel.objects.get(poster="user@example.com") self.assertEqual(comment.text, "text body") ``` -------------------------------- ### SparkPost Stored Template with Merge Data Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sparkpost.md Example of sending an email using a SparkPost stored template ID and providing per-recipient and global merge data. Note that when using a template_id, from_email must be set to None. ```python message = EmailMessage( ... to=["alice@example.com", "Bob "] ) message.template_id = "11806290401558530" # SparkPost id message.from_email = None # must set after constructor (see below) message.merge_data = { 'alice@example.com': {'name': "Alice", 'order_no": "12345"}, 'bob@example.com': {'name': "Bob", 'order_no": "54321"}, } message.merge_global_data = { 'ship_date': "May 15", # Can use SparkPost's special "dynamic" keys for nested substitutions (see notes): 'dynamic_html': { 'status_html': "Status", }, 'dynamic_plain': { 'status_plain': "Status: https://example.com/order/{{order_no}}", }, } ``` -------------------------------- ### Configure Anymail with a single dictionary Source: https://github.com/anymail/django-anymail/blob/main/docs/installation.md Use the ANYMAIL dictionary in settings.py to configure global Anymail settings and defaults. ```python ANYMAIL = { "MAILGUN_API_KEY": "12345", "SEND_DEFAULTS": { "tags": ["myapp"] }, } ``` -------------------------------- ### Set Brevo API URL Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/brevo.md Optionally configure the base URL for the Brevo API. The default is usually sufficient. ```python BREVO_API_URL = "https://api.brevo.com/v3/" ``` -------------------------------- ### Set SparkPost Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/sparkpost.md Configure Django's EMAIL_BACKEND setting to use Anymail's SparkPost integration. ```python EMAIL_BACKEND = "anymail.backends.sparkpost.EmailBackend" ``` -------------------------------- ### Formatting Non-String Merge Data Source: https://github.com/anymail/django-anymail/blob/main/docs/sending/templates.md Format non-string data types like Decimal and date objects into strings before assigning them to merge data to avoid serialization errors. This example shows formatting for a product name, a decimal cost, and a date. ```python product = Product.objects.get(123) # A Django model total_cost = Decimal('19.99') ship_date = date(2015, 11, 18) # Do something this instead: message.merge_global_data = { 'PRODUCT': product.name, # assuming name is a CharField 'TOTAL_COST': "{cost:0.2f}".format(cost=total_cost), 'SHIP_DATE': ship_date.strftime('%B %d, %Y') # US-style "March 15, 2015" } ``` -------------------------------- ### Configure Mailtrap Backend and API Settings Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/mailtrap.md Set the email backend to Mailtrap and provide your API token and optionally a sandbox ID for testing. ```python EMAIL_BACKEND = "anymail.backends.mailtrap.EmailBackend" ANYMAIL = { "MAILTRAP_API_TOKEN": "", # Only to use the Email Sandbox service: "MAILTRAP_SANDBOX_ID": , } ``` -------------------------------- ### Set Brevo Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/brevo.md Configure your Django settings to use Anymail's Brevo email backend. ```python EMAIL_BACKEND = "anymail.backends.brevo.EmailBackend" ``` -------------------------------- ### Run Linting and Specific Django/Python Version Tests with Tox Source: https://github.com/anymail/django-anymail/blob/main/docs/contributing.md Execute linting and specific combinations of Django and Python versions for testing. ```console tox -e lint,django60-py314-all,django50-py310-all,docs ``` -------------------------------- ### Set Postmark Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/postmark.md Configure your Django settings to use Anymail's Postmark email backend. ```python EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend" ``` -------------------------------- ### Sending Emails with Different Django Backends Source: https://github.com/anymail/django-anymail/blob/main/docs/tips/multiple_backends.md Demonstrates sending emails using Django's default Anymail backend, a standard SMTP backend, and another Anymail backend (Mailtrap) by explicitly specifying the connection. This is useful for routing different types of emails to distinct services. ```python from django.core.mail import send_mail, get_connection # send_mail connection defaults to the settings EMAIL_BACKEND, which # we've set to Anymail's Mailgun EmailBackend. This will be sent using Mailgun: send_mail("Thanks", "We sent your order", "sales@example.com", ["customer@example.com"]) # Get a connection to an SMTP backend, and send using that instead: smtp_backend = get_connection('django.core.mail.backends.smtp.EmailBackend') send_mail("Uh-Oh", "Need your attention", "admin@example.com", ["alert@example.com"], connection=smtp_backend) # You can even use multiple Anymail backends in the same app: mailtrap_backend = get_connection('anymail.backends.mailtrap.EmailBackend') send_mail("Password reset", "Here you go", "noreply@example.com", ["user@example.com"], connection=mailtrap_backend) ``` -------------------------------- ### Set Anymail Backend for Scaleway Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/scaleway.md Configure Django's email backend to use Anymail's Scaleway integration. This is the primary step to enable Scaleway TEM. ```python EMAIL_BACKEND = "anymail.backends.scaleway.EmailBackend" ``` -------------------------------- ### Set Postmark API URL Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/postmark.md Configure the base URL for the Postmark API. The default is usually sufficient. ```python POSTMARK_API_URL = "https://api.postmarkapp.com/" ``` -------------------------------- ### Override settings per instance Source: https://github.com/anymail/django-anymail/blob/main/docs/installation.md Advanced usage allows overriding settings like API keys on a per-instance basis when initializing email backends. ```python get_connection("anymail.backends.mailgun.EmailBackend", api_key="abc") ``` -------------------------------- ### Simulate Anymail Tracking Events in Tests Source: https://github.com/anymail/django-anymail/blob/main/docs/tips/testing.md Use this snippet to simulate Anymail tracking events and send them to your signal receivers. It requires importing AnymailTrackingEvent and the tracking signal. ```python from anymail.signals import AnymailTrackingEvent, tracking from django.test import TestCase class EmailTrackingTests(TestCase): def test_delivered_event(self): # Build an AnymailTrackingEvent with event_type (required) # and any other attributes your receiver cares about. E.g.: event = AnymailTrackingEvent( event_type="delivered", recipient="to@example.com", message_id="test-message-id", ) # Invoke all registered Anymail tracking signal receivers: tracking.send(sender=object(), event=event, esp_name="TestESP") # Verify expected behavior of your receiver. What to test here # depends on how your code handles the tracking events. E.g., if # you create a Django model to store the event, you might check: from myapp.models import MyTrackingModel self.assertTrue(MyTrackingModel.objects.filter( email="to@example.com", event="delivered", message_id="test-message-id", ).exists()) def test_bounced_event(self): # ... as above, but with `event_type="bounced"` # etc. ``` -------------------------------- ### Set Mailjet Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/mailjet.md Configure Django's EMAIL_BACKEND setting to use Anymail's Mailjet integration. ```python EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend" ``` -------------------------------- ### Set Mailgun Email Backend Source: https://github.com/anymail/django-anymail/blob/main/docs/esps/mailgun.md To use Anymail's Mailgun backend, set this in your settings.py. ```python EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" ```