Postmark

Anymail integrates with the ActiveCampaign Postmark transactional email service, using their HTTP email API.

Settings

EMAIL_BACKEND

To use Anymail’s Postmark backend, set:

EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend"

in your settings.py.

POSTMARK_SERVER_TOKEN

Required. A Postmark server token.

ANYMAIL = {
    ...
    "POSTMARK_SERVER_TOKEN": "<your server token>",
}

Anymail will also look for POSTMARK_SERVER_TOKEN at the root of the settings file if neither ANYMAIL["POSTMARK_SERVER_TOKEN"] nor ANYMAIL_POSTMARK_SERVER_TOKEN is set.

You can override the server token for an individual message in its esp_extra.

POSTMARK_API_URL

The base url for calling the Postmark API.

The default is POSTMARK_API_URL = "https://api.postmarkapp.com/" (It’s unlikely you would need to change this.)

esp_extra support

To use Postmark features not directly supported by Anymail, you can set a message’s esp_extra to a dict that will be merged into the json sent to Postmark’s email API.

Example:

message.esp_extra = {
    'MessageStream': 'marketing',  # send using specific message stream ID
    'server_token': '<API server token for just this message>',
}

(You can also set "esp_extra" in Anymail’s global send defaults to apply it to all messages.)

Limitations and quirks

Postmark does not support a few tracking and reporting additions offered by other ESPs.

Anymail normally raises an AnymailUnsupportedFeature error when you try to send a message using features that Postmark doesn’t support You can tell Anymail to suppress these errors and send the messages anyway – see Unsupported features.

Single tag

Postmark allows a maximum of one tag per message. If your message has two or more tags, you’ll get an AnymailUnsupportedFeature error—or if you’ve enabled ANYMAIL_IGNORE_UNSUPPORTED_FEATURES, Anymail will use only the first tag.

No delayed sending

Postmark does not support send_at.

Click-tracking

Postmark supports several link-tracking options. Anymail treats track_clicks as Postmark’s “HtmlAndText” option when True.

If you would prefer Postmark’s “HtmlOnly” or “TextOnly” link-tracking, you could either set that as a Postmark server-level default (and use message.track_clicks = False to disable tracking for specific messages), or use something like message.esp_extra = {'TrackLinks': "HtmlOnly"} to specify a particular option.

Open-tracking

To control track_opens on individual messages, you must disable Postmark’s server-level default and then set track_opens = True on all messages that should have open tracking. (A message-level track_opens = False cannot override open tracking if enabled in Postmark’s server defaults.)

If most of your messages should be sent with open tracking, you can use Anymail’s global send defaults (rather than Postmark’s server-level setting):

# settings.py
ANYMAIL = {
    # ...
    "SEND_DEFAULTS": { "track_opens": True },
}

Individual messages can then use track_opens = False to override Anymail’s default.

No envelope sender overrides

Postmark does not support overriding envelope_sender on individual messages. (You can configure custom return paths for each sending domain in the Postmark control panel.)

Batch sending/merge and ESP templates

Postmark offers both ESP stored templates and batch sending with per-recipient merge data.

Changed in version 4.2: Added Postmark merge_data and batch sending support. (Earlier Anymail releases only supported merge_global_data with Postmark.)

To use a Postmark template, set the message’s template_id to either the numeric Postmark “TemplateID” or its string “TemplateAlias” (which is not the template’s name). You can find a template’s numeric id near the top right in Postmark’s template editor, and set the alias near the top right above the name.

Changed in version 5.0: Earlier Anymail releases only allowed numeric template IDs.

Supply the Postmark “TemplateModel” variables using Anymail’s normalized merge_data and merge_global_data message attributes:

message = EmailMessage(
    # (subject and body come from the template, so don't include those)
    to=["[email protected]", "Bob <[email protected]>"]
)
message.template_id = 80801  # Postmark template id or alias
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",
}

Postmark does not allow overriding the message’s subject or body with a template. (You can customize the subject by including variables in the template’s subject.)

When you supply per-recipient merge_data, Anymail automatically switches to Postmark’s batch send API, so that each “to” recipient sees only their own email address. (Any cc’s or bcc’s will be duplicated for every to-recipient.)

If you want to use batch sending with a regular message (without a template), set merge data to an empty dict: message.merge_data = {}.

See this Postmark blog post on templates for more information.

Status tracking webhooks

If you are using Anymail’s normalized status tracking, set up a webhook in your Postmark account settings, under Servers > your server name > Settings > Webhooks. The webhook URL is:

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

Choose all the event types you want to receive. Anymail doesn’t care about the “include messsage content” and “post only on first open” options; whether to use them is your choice.

If you use multiple Postmark servers, you’ll need to repeat entering the webhook settings for each of them.

Postmark will report these Anymail event_types: rejected, failed, bounced, deferred, delivered, autoresponded, opened, clicked, complained, unsubscribed, subscribed. (Postmark does not support sent–what it calls “processed”–events through webhooks.)

The event’s esp_event field will be a dict of Postmark delivery, bounce, spam-complaint, open-tracking, or click data.

Inbound webhook

To receive email from Postmark through Anymail’s normalized inbound handling, follow Postmark’s guide to Configure an inbound server that posts to Anymail’s inbound webhook.

In their step 4, set the inbound webhook URL to:

https://random:random@yoursite.example.com/anymail/postmark/inbound/

We recommend enabling the “Include raw email content in JSON payload” checkbox. Anymail’s inbound handling supports either choice, but raw email is preferred to get the most accurate representation of any received message. (If you are using Postmark’s server API, this is the RawEmailEnabled option.)

Changed in version 10.0: Added handling for Postmark’s “include raw email content”.

You may also want to read through the “Inbound domain forwarding” and “Configure inbound blocking” sections of Postmark’s Inbound Processing guide.