Mailjet
Anymail integrates with the Mailjet email service, using their transactional Send API v3.1.
Changed in version 8.0: Earlier Anymail versions used Mailjet’s older Send API v3. The change to v3.1 fixes some limitations of the earlier API, and should only affect your code if you use Anymail’s esp_extra feature to set API-specific options or if you are trying to send messages with multiple reply-to addresses.
Settings
EMAIL_BACKEND
To use Anymail’s Mailjet backend, set:
EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend"
in your settings.py.
MAILJET_API_KEY and MAILJET_SECRET_KEY
Your Mailjet API key and secret key, from your Mailjet account REST API settings under API Key Management. (Mailjet’s documentation also sometimes uses “API private key” to mean the same thing as “secret key.”)
ANYMAIL = { ... "MAILJET_API_KEY": "<your API key>", "MAILJET_SECRET_KEY": "<your API secret>", }
You can use either the main account or a sub-account API key.
Anymail will also look for MAILJET_API_KEY
and MAILJET_SECRET_KEY
at the
root of the settings file if neither ANYMAIL["MAILJET_API_KEY"]
nor ANYMAIL_MAILJET_API_KEY
is set.
MAILJET_API_URL
The base url for calling the Mailjet API.
The default is MAILJET_API_URL = "https://api.mailjet.com/v3.1/"
(It’s unlikely you would need to change this.)
esp_extra support
To use Mailjet features not directly supported by Anymail, you can
set a message’s esp_extra
to
a dict
of Mailjet’s Send API body parameters.
Your esp_extra
dict will be deeply merged into the Mailjet
API payload, with esp_extra
having precedence in conflicts.
(Note that it’s not possible to merge into the "Messages"
key;
any value you supply would override "Messages"
completely. Use "Globals"
for options to apply to all messages.)
Example:
message.esp_extra = { # Most "Messages" options can be included under Globals: "Globals": { "Priority": 3, # Use Mailjet critically-high priority queue "TemplateErrorReporting": {"Email": "[email protected]"}, }, # A few options must be at the root: "SandboxMode": True, "AdvanceErrorHandling": True, # *Don't* try to set Messages: # "Messages": [... this would override *all* recipients, not be merged ...] }
(You can also set "esp_extra"
in Anymail’s global send defaults
to apply it to all messages.)
Limitations and quirks
- Single reply_to
Mailjet’s API only supports a single Reply-To email address. If your message has two or more, you’ll get an
AnymailUnsupportedFeature
error—or if you’ve enabledANYMAIL_IGNORE_UNSUPPORTED_FEATURES
, Anymail will use only the firstreply_to
address.- Single tag
Anymail uses Mailjet’s campaign option for tags, and Mailjet allows only a single campaign per message. If your message has two or more
tags
, you’ll get anAnymailUnsupportedFeature
error—or if you’ve enabledANYMAIL_IGNORE_UNSUPPORTED_FEATURES
, Anymail will use only the first tag.
- No delayed sending
Mailjet does not support
send_at
.- Envelope sender may require approval
Anymail passes
envelope_sender
to Mailjet, but this may result in an API error if you have not received special approval from Mailjet support to use custom senders.- message_id is MessageID (not MessageUUID)
Mailjet’s Send API v3.1 returns both a “legacy” MessageID and a newer MessageUUID for each successfully sent message. Anymail uses the MessageID as the
message_id
when reporting ESP send status, because Mailjet’s other (statistics, event tracking) APIs don’t yet support MessageUUID.
Older limitations
Changed in version 6.0: Earlier versions of Anymail were unable to mix cc
or bcc
fields
and merge_data
in the same Mailjet message.
This limitation was removed in Anymail 6.0.
Changed in version 8.0: Earlier Anymail versions had special handling to work around a Mailjet v3 API bug with commas in recipient display names. Anymail 8.0 uses Mailjet’s v3.1 API, which does not have the bug.
Batch sending/merge and ESP templates
Mailjet offers both ESP stored templates and batch sending with per-recipient merge data.
When you send a message with multiple to
addresses, the
merge_data
determines how many
distinct messages are sent:
If
merge_data
is not set (the default), Anymail will tell Mailjet to send a single message, and all recipients will see the complete list of To addresses.If
merge_data
is set—even to an empty{}
dict, Anymail will tell Mailjet to send a separate message for eachto
address, and the recipients won’t see the other To addresses.
You can use a Mailjet stored transactional template by setting a message’s
template_id
to the
template’s numeric template ID. (Not the template’s name. To get the
numeric template id, click on the name in your Mailjet transactional templates,
then look for “Template ID” above the preview that appears.)
Supply the template merge data values with Anymail’s
normalized merge_data
and merge_global_data
message attributes.
message = EmailMessage( ... # omit subject and body (or set to None) to use template content to=["[email protected]", "Bob <[email protected]>"] ) message.template_id = "176375" # Mailjet numeric template id message.from_email = None # Use the From address stored with the template message.merge_data = { '[email protected]': {'name': "Alice", 'order_no': "12345"}, '[email protected]': {'name': "Bob", 'order_no': "54321"}, } message.merge_global_data = { 'ship_date': "May 15", }
Any from_email
in your EmailMessage will override the template’s default sender
address. To use the template’s sender, you must explicitly set from_email = None
after creating the EmailMessage, as shown above. (If you omit this, Django’s default
DEFAULT_FROM_EMAIL
will be used.)
Instead of creating a stored template at Mailjet, you can also refer to merge fields directly in an EmailMessage’s body—the message itself is used as an on-the-fly template:
message = EmailMessage( from_email="[email protected]", to=["[email protected]", "Bob <[email protected]>"], subject="Your order has shipped", # subject doesn't support on-the-fly merge fields # Use [[var:FIELD]] to for on-the-fly merge into plaintext or html body: body="Dear [[var:name]]: Your order [[var:order_no]] shipped on [[var:ship_date]]." ) message.merge_data = { '[email protected]': {'name': "Alice", 'order_no': "12345"}, '[email protected]': {'name': "Bob", 'order_no': "54321"}, } message.merge_global_data = { 'ship_date': "May 15", }
(Note that on-the-fly templates use square brackets to indicate “personalization” merge fields, rather than the curly brackets used with stored templates in Mailjet’s template language.)
See Mailjet’s template documentation and template language docs for more information.
Status tracking webhooks
If you are using Anymail’s normalized status tracking, enter the url in your Mailjet account REST API settings under Event tracking (triggers):
https://random:random@yoursite.example.com/anymail/mailjet/tracking/
random:random is an
ANYMAIL_WEBHOOK_SECRET
shared secretyoursite.example.com is your Django site
Be sure to enter the URL in the Mailjet settings for all the event types you want to receive. It’s also recommended to select the “group events” checkbox for each trigger, to minimize your server load.
Mailjet will report these Anymail event_type
s:
rejected, bounced, deferred, delivered, opened, clicked, complained, unsubscribed.
The event’s esp_event
field will be
a dict
of Mailjet event fields, for a single event. (Although Mailjet calls
webhooks with batches of events, Anymail will invoke your signal receiver separately
for each event in the batch.)
Inbound webhook
If you want to receive email from Mailjet through Anymail’s normalized inbound handling, follow Mailjet’s Parse API inbound emails guide to set up Anymail’s inbound webhook.
The parseroute Url parameter will be:
https://random:random@yoursite.example.com/anymail/mailjet/inbound/
random:random is an
ANYMAIL_WEBHOOK_SECRET
shared secretyoursite.example.com is your Django site
Once you’ve done Mailjet’s “basic setup” to configure the Parse API webhook, you can skip ahead to the “use your own domain” section of their guide. (Anymail normalizes the inbound event for you, so you won’t need to worry about Mailjet’s event and attachment formats.)