Unisender Go

Anymail supports sending email from Django through the Unisender Go email service, using their Web API v1.

Settings

EMAIL_BACKEND

To use Anymail’s Unisender Go backend, set:

EMAIL_BACKEND = "anymail.backends.unisender_go.EmailBackend"

in your settings.py.

UNISENDER_GO_API_KEY, UNISENDER_GO_API_URL

Required—the API key and API endpoint for your Unisender Go account or project:

ANYMAIL = {
    "UNISENDER_GO_API_KEY": "<your API key>",
    # Pick ONE of these, depending on your account (go1 vs. go2):
    "UNISENDER_GO_API_URL": "https://go1.unisender.ru/ru/transactional/api/v1/",
    "UNISENDER_GO_API_URL": "https://go2.unisender.ru/ru/transactional/api/v1/",
}

Get the API key from Unisender Go’s dashboard under Account > Security > API key (Учетная запись > Безопасность > API-ключ). Or for a project-level API key, under Settings > Projects (Настройки > Проекты).

The correct API URL depends on which Unisender Go data center registered your account. You must specify the full, versioned Unisender Go API endpoint as shown above (not just the base uri).

If trying to send mail raises an API Error “User with id … not found” (code 114), the likely cause is using the wrong API URL for your account. (To find which server handles your account, log into Unisender Go’s dashboard and then check hostname in your browser’s URL.)

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

UNISENDER_GO_GENERATE_MESSAGE_ID

Whether Anymail should generate a separate UUID for each recipient when sending messages through Unisender Go, to facilitate status tracking. The UUIDs are attached to the message as recipient metadata named “anymail_id” and available in anymail_status.recipients[recipient_email].message_id on the message after it is sent.

Default True. You can set to False to disable generating UUIDs:

ANYMAIL = {
    ...
    "UNISENDER_GO_GENERATE_MESSAGE_ID": False
}

When disabled, each sent message will use Unisender Go’s “job_id” as the (single) message_id for all recipients. (The job_id alone may be sufficient for your tracking needs, particularly if you only send to one recipient per message.)

Additional sending options and esp_extra

Unisender Go offers a number of additional options you may want to use when sending a message. You can set these for individual messages using Anymail’s esp_extra. See the full list of options in Unisender Go’s email/send.json API documentation.

For example:

message = EmailMessage(...)
message.esp_extra = {
    "global_language": "en",  # Use English text for unsubscribe link
    "bypass_global": 1,  # Ignore system level blocked address list
    "bypass_unavailable": 1,  # Ignore account level blocked address list
    "options": {
        # Custom unsubscribe link (can use merge_data {{substitutions}}):
        "unsubscribe_url": "https://example.com/unsub?u={{subscription_id}}",
        "custom_backend_id": 22,  # ID of dedicated IP address
    }
}

(Note that you do not include the API’s root level "message" key in esp_extra, but you must include any nested keys—like "options" in the example above—to match Unisender Go’s API structure.)

To set default esp_extra options for all messages, use Anymail’s global send defaults in your settings.py. Example:

ANYMAIL = {
    ...,
    "UNISENDER_GO_SEND_DEFAULTS": {
        "esp_extra": {
            # Omit the unsubscribe link for all sent messages:
            "skip_unsubscribe": 1
        }
    }
}

Any options set in an individual message’s esp_extra take precedence over the global send defaults.

For many of these additional options, you will need to contact Unisender Go tech support for approval before being able to use them.

Limitations and quirks

Attachment filename restrictions

Unisender Go does not permit the slash character (/) in attachment filenames. Trying to send one will result in an AnymailAPIError.

Restrictions on to, cc and bcc

For non-batch sends, Unisender Go has a limit of 10 recipients each for to, cc and bcc. Unisender Go does not support cc-only or bcc-only messages. All bcc recipients must be in a domain you have verified with Unisender Go.

For batch sending (with Anymail’s merge_data or merge_metadata), Unisender Go has a limit of 500 to recipients in a single message.

Unisender Go’s API does not support cc with batch sending. Trying to include cc recipients in a batch send will raise an AnymailUnsupportedFeature error. (If you’ve enabled ANYMAIL_IGNORE_UNSUPPORTED_FEATURES, Anymail will handle cc in a Unisender Go batch send as additional to recipients.)

With batch sending, Unisender Go effectively treats bcc recipients as additional to recipients, which may not behave as you’d expect. Each bcc in a batch send will be sent a single copy of the message, with the bcc’s email in the To header, and personalized using merge_data for their own email address, if any. (Unlike some other ESPs, bcc recipients in a batch send won’t receive a separate copy of the message personalized for each to email.)

AMP for Email

Unisender Go supports sending AMPHTML email content. To include it, use message.attach_alternative("...AMPHTML content...", "text/x-amp-html") (and be sure to also include regular HTML and text bodies, too).

Use metadata for campaign_id

If you want to use Unisender Go’s campaign_id, set it in Anymail’s metadata.

Duplicate emails ignored

Unisender Go only allows an email address to be included once in a message’s combined to, cc and bcc lists. If the same email appears multiple times, the additional instances are ignored. (Unisender Go reports them as duplicates, but Anymail does not treat this as an error.)

Note that email addresses are case-insensitive.

Anymail’s message_id is passed in recipient metadata

By default, Anymail generates a unique identifier for each to recipient in a message, and (effectively) adds this to the recipients’ merge_metadata with the key "anymail_id".

This feature consumes one of Unisender Go’s 10 available metadata slots. To disable it, see the UNISENDER_GO_GENERATE_MESSAGE_ID setting.

Recipient display names are set in merge_data

To include a display name (“friendly name”) with a to email address, Unisender Go’s Web API uses an entry in their per-recipient template “substitutions,” which are also used for Anymail’s merge_data.

To avoid conflicts, do not use "to_name" as a key in merge_data or merge_global_data.

Limited merge headers support

Unisender Go supports per-recipient List-Unsubscribe headers (if your account has been approved to disable their unsubscribe link), but trying to include any other field in Anymail’s merge_headers will raise an AnymailUnsupportedFeature error.

No envelope sender overrides

Unisender Go does not support overriding a message’s envelope_sender.

Batch sending/merge and ESP templates

Unisender Go supports ESP stored templates, on-the-fly templating, and batch sending with per-recipient merge data substitutions.

To send using a template you have created in your Unisender Go account, set the message’s template_id to the template’s ID. (This is a UUID found at the top of the template’s “Properties” page—not the template name.)

To supply template substitution data, use Anymail’s normalized merge_data and merge_global_data message attributes. You can also use merge_metadata to supply custom tracking data for each recipient.

Here is an example using a template that has slots for {{name}}, {{order_no}}, and {{ship_date}} substitution data:

message = EmailMessage(
    to=["[email protected]", "Bob <[email protected]>"],
)
message.from_email = None  # Use template From email and name
message.template_id = "0000aaaa-1111-2222-3333-4444bbbbcccc"
message.merge_data = {
    "[email protected]": {"name": "Alice", "order_no": "12345"},
    "[email protected]": {"name": "Bob", "order_no": "54321"},
}
message.merge_global_data = {
    "ship_date": "15-May",
}
message.send()

Any subject provided will override the one defined in the template. The message’s from_email (which defaults to your DEFAULT_FROM_EMAIL setting) will override the template’s default sender. If you want to use the From email and name defined with the template, be sure to set from_email to None after creating the message, as shown above.

Unisender Go also supports inline, on-the-fly templates. Here is the same example using inline templates:

message = EmailMessage(
    from_email="[email protected]",
    to=["[email protected]", "Bob <[email protected]>"],
    # 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 = {
    "[email protected]": {"name": "Alice", "order_no": "12345"},
    "[email protected]": {"name": "Bob", "order_no": "54321"},
}
message.merge_global_data = {
    "ship_date": "May 15",
}
message.send()

Note that Unisender Go doesn’t allow whitespace in the substitution braces: {{order_no}} works, but {{ order_no }} causes an error.

There are two available Unisender Go template engines: “simple” and “velocity.” For templates stored in your account, you select the engine in the template’s properties. Inline templates use the simple engine by default; you can select “velocity” using esp_extra:

message.esp_extra = {
    "template_engine": "velocity",
}
message.subject = "Your order $order_no has shipped"  # Velocity syntax

When you set per-recipient merge_data or merge_metadata, Anymail will use batch sending mode so that each to recipient sees only their own email address. You can set either of these attributes to an empty dict (message.merge_data = {}) to force batch sending for a message that wouldn’t otherwise use it.

Be sure to review the restrictions above before trying to use cc or bcc with Unisender Go batch sending.

Status tracking webhooks

If you are using Anymail’s normalized status tracking, add the url in Unisender Go’s dashboard under Settings > Webhooks (Настройки > Вебхуки). Create a webhook with these settings:

  • Notification Url:

    https://yoursite.example.com/anymail/unisender_go/tracking/

    where yoursite.example.com is your Django site.

  • Status: set to “Active” if you have already deployed your Django project with Anymail installed. Otherwise set to “Inactive” and wait to activate it until you deploy.

    (Unisender Go performs a GET request to verify the webhook URL when it is marked active.)

  • Event format: “json_post”

    (If your gateway handles decompressing incoming request bodies—e.g., Apache with a mod_deflate input filter—you could also use “json_post_compressed.” Most web servers do not handle compressed input by default.)

  • Events: your choice. Anymail supports any combination of sent, delivered, soft_bounced, hard_bounced, opened, clicked, unsubscribed, subscribed, and/or spam.

    Anymail does not support Unisender Go’s spam_block events (but will ignore them if you accidentally include it).

  • Number of simultaneous requests: depends on your web server’s capacity

    Most deployments should be able to handle the default 10. But you may need to use a smaller number if your tracking signal receiver uses a lot of resources (or monopolizes your database), or if your web server isn’t configured to handle that many simultaneous requests (including requests from your site users).

  • Use single event: the default “No” is recommended

    Anymail can process multiple events in a single webhook call. It invokes your signal receiver separately for each event. But all of the events in the call (up to 100 when set to “No”) must be handled within 3 seconds total, or Unisender Go will think the request failed and resend it.

    If your tracking signal receiver takes a long time to process each event, you may need to change “Use single event” to “Yes” (one event per webhook call).

  • Additional information about delivery: “Yes” is recommended

    (If you set this to “No”, your tracking events won’t include mta_response, user_agent or click_url.)

  • Selected project: Must match the project for your Anymail UNISENDER_GO_API_KEY setting, if projects are enabled for your account and you are using a project level API key. Leave blank if you are using your account level API key.

    This affects webhook signing. If the selected project does not match your API key, you’ll get AnymailWebhookValidationFailure errors.

Note that Unisender Go does not deliver tracking events for recipient addresses that are blocked at send time. You must check the message’s anymail_status.recipients[recipient_email].status immediately after sending to detect rejected recipients.

Unisender Go implements webhook signing on the entire event payload, and Anymail verifies this signature using your UNISENDER_GO_API_KEY. It is not necessary to use an ANYMAIL_WEBHOOK_SECRET with Unisender Go, but if you have set one, you must include the random:random shared secret in the Notification URL like this:

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

In your tracking signal receiver, the event’s esp_event field will be the "event_data" object from a single, raw “transactional_email_status” event. For example, you could get the IP address that opened a message using event.esp_event["delivery_info"]["ip"].

(Anymail does not handle Unisender Go’s “transactional_spam_block” events, and will filter these without calling your tracking signal handler.)

Inbound webhook

Unisender Go does not currently offer inbound email.

(If this changes in the future, please open an issue so we can add support in Anymail.)