Event-tracking webhooks

Closes #3
This commit is contained in:
medmunds
2016-04-20 17:13:55 -07:00
parent 36461e57b9
commit d3f914be12
31 changed files with 2451 additions and 428 deletions

View File

@@ -36,13 +36,13 @@ Email Service Provider |Mailgun| |Mandrill| |Postmark|
:attr:`~AnymailMessage.track_clicks` Yes Yes No Yes
:attr:`~AnymailMessage.track_opens` Yes Yes Yes Yes
.. rubric:: :ref:`Status <esp-send-status>` and tracking
.. rubric:: :ref:`Status <esp-send-status>` and :ref:`event tracking <event-tracking>`
-------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes
|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes
=========================================== ========= ========== ========== ==========
.. Status tracking webhooks (coming)...
.. .. rubric:: :ref:`inbound`
.. -------------------------------------------------------------------------------------------
.. Inbound webhooks (coming)...
@@ -56,6 +56,7 @@ meaningless. (And even specific features don't matter if you don't plan to use t
.. |Mandrill| replace:: :ref:`mandrill-backend`
.. |Postmark| replace:: :ref:`postmark-backend`
.. |SendGrid| replace:: :ref:`sendgrid-backend`
.. |AnymailTrackingEvent| replace:: :class:`~anymail.signals.AnymailTrackingEvent`
Other ESPs

View File

@@ -103,3 +103,35 @@ values directly to Mailgun. You can use any of the (non-file) parameters listed
}
.. _Mailgun sending docs: https://documentation.mailgun.com/api-sending.html#sending
.. _mailgun-webhooks:
Status tracking webhooks
------------------------
If you are using Anymail's normalized :ref:`status tracking <event-tracking>`, enter
the url in your `Mailgun dashboard`_ on the "Webhooks" tab. Mailgun allows you to enter
a different URL for each event type: just enter this same Anymail tracking URL
for all events you want to receive:
:samp:`https://{random}:{random}@{yoursite.example.com}/anymail/mailgun/tracking/`
* *random:random* is an :setting:`ANYMAIL_WEBHOOK_AUTHORIZATION` shared secret
* *yoursite.example.com* is your Django site
If you use multiple Mailgun sending domains, you'll need to enter the webhook
URLs for each of them, using the selector on the left side of Mailgun's dashboard.
Mailgun implements a limited form of webhook signing, and Anymail will verify
these signatures (based on your :setting:`MAILGUN_API_KEY <ANYMAIL_MAILGUN_API_KEY>`
Anymail setting).
Mailgun will report these Anymail :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s:
delivered, rejected, bounced, complained, unsubscribed, opened, clicked.
The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be
a Django :class:`~django.http.QueryDict` object of `Mailgun event fields`_.
.. _Mailgun dashboard: https://mailgun.com/app/dashboard
.. _Mailgun event fields: https://documentation.mailgun.com/user_manual.html#webhooks

View File

@@ -56,6 +56,31 @@ root of the settings file if neither ``ANYMAIL["MANDRILL_API_KEY"]``
nor ``ANYMAIL_MANDRILL_API_KEY`` is set.
.. setting:: ANYMAIL_MANDRILL_WEBHOOK_KEY
.. rubric:: MANDRILL_WEBHOOK_KEY
Required if using Anymail's webhooks. The "webhook authentication key"
issued by Mandrill.
`More info <https://mandrill.zendesk.com/hc/en-us/articles/205583257>`_
in Mandrill's KB.
.. setting:: ANYMAIL_MANDRILL_WEBHOOK_URL
.. rubric:: MANDRILL_WEBHOOK_URL
Required only if using Anymail's webhooks *and* the hostname your
Django server sees is different from the public webhook URL
you provided Mandrill. (E.g., if you have a proxy in front
of your Django server that forwards
"https\://yoursite.example.com" to "http\://localhost:8000/").
If you are seeing :exc:`AnymailWebhookValidationFailure` errors
from your webhooks, set this to the exact webhook URL you entered
in Mandrill's settings.
.. setting:: ANYMAIL_MANDRILL_API_URL
.. rubric:: MANDRILL_API_URL
@@ -76,6 +101,41 @@ Anymail's Mandrill backend does not yet implement the
:attr:`~anymail.message.AnymailMessage.esp_extra` feature.
.. _mandrill-webhooks:
Status tracking webhooks
------------------------
If you are using Anymail's normalized :ref:`status tracking <event-tracking>`,
follow `Mandrill's instructions`_ to add Anymail's webhook URL:
:samp:`https://{random}:{random}@{yoursite.example.com}/anymail/mandrill/tracking/`
* *random:random* is an :setting:`ANYMAIL_WEBHOOK_AUTHORIZATION` shared secret
* *yoursite.example.com* is your Django site
Be sure to check the boxes in the Mandrill settings for the event types you want to receive.
The same Anymail tracking URL can handle all Mandrill "message" and "sync" events.
Mandrill implements webhook signing on the entire event payload, and Anymail will
verify the signature. You must set :setting:`ANYMAIL_MANDRILL_WEBHOOK_KEY` to the
webhook key authentication key issued by Mandrill. You may also need to set
:setting:`ANYMAIL_MANDRILL_WEBHOOK_URL` depending on your server config.
Mandrill will report these Anymail :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s:
sent, rejected, deferred, bounced, opened, clicked, complained, unsubscribed. Mandrill does
not support delivered events. Mandrill "whitelist" and "blacklist" sync events will show up
as Anymail's unknown event_type.
The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be
a `dict` of Mandrill event fields, for a single event. (Although Mandrill calls
webhooks with batches of events, Anymail will invoke your signal receiver separately
for each event in the batch.)
.. _Mandrill's instructions:
https://mandrill.zendesk.com/hc/en-us/articles/205583217-Introduction-to-Webhooks
.. _migrating-from-djrill:
Migrating from Djrill
@@ -123,6 +183,16 @@ Changes to settings
(or just `IGNORE_RECIPIENT_STATUS` in the :setting:`ANYMAIL`
settings dict).
``DJRILL_WEBHOOK_SECRET`` and ``DJRILL_WEBHOOK_SECRET_NAME``
Replaced with HTTP basic auth. See :ref:`securing-webhooks`.
``DJRILL_WEBHOOK_SIGNATURE_KEY``
Use :setting:`ANYMAIL_MANDRILL_WEBHOOK_KEY` instead.
``DJRILL_WEBHOOK_URL``
Use :setting:`ANYMAIL_MANDRILL_WEBHOOK_URL`, or eliminate if
your Django server is not behind a proxy that changes hostnames.
Changes to EmailMessage attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -182,3 +252,25 @@ Changes to EmailMessage attributes
Or better yet, use Anymail's new :ref:`inline-images`
helper functions to attach your inline images.
Changes to webhooks
~~~~~~~~~~~~~~~~~~~
Anymail uses HTTP basic auth as a shared secret for validating webhook
calls, rather than Djrill's "secret" query parameter. See
:ref:`securing-webhooks`. (A slight advantage of basic auth over query
parameters is that most logging and analytics systems are aware of the
need to keep auth secret.)
Anymail replaces `djrill.signals.webhook_event` with
`anymail.signals.tracking` and (in a future release)
`anymail.signals.inbound`. Anymail parses and normalizes
the event data passed to the signal receiver: see :ref:`event-tracking`.
The equivalent of Djrill's ``data`` parameter is available
to your signal receiver as
:attr:`event.esp_event <anymail.signals.AnymailTrackingEvent.esp_event>`,
and for most events, the equivalent of Djrill's ``event_type`` parameter
is `event.esp_event['event']`. But consider working with Anymail's
normalized :class:`~anymail.signals.AnymailTrackingEvent` instead.

View File

@@ -113,3 +113,37 @@ see :ref:`unsupported-features`.
**No delayed sending**
Postmark does not support :attr:`~anymail.message.AnymailMessage.send_at`.
.. _postmark-webhooks:
Status tracking webhooks
------------------------
If you are using Anymail's normalized :ref:`status tracking <event-tracking>`, enter
the url in your `Postmark account settings`_, under Servers > *your server name* >
Settings > Outbound > Webhooks. You should enter this same Anymail tracking URL
for both the "Bounce webhook" and "Opens webhook" (if you want to receive both
types of events):
:samp:`https://{random}:{random}@{yoursite.example.com}/anymail/postmark/tracking/`
* *random:random* is an :setting:`ANYMAIL_WEBHOOK_AUTHORIZATION` shared secret
* *yoursite.example.com* is your Django site
Anymail doesn't care about the "include bounce content" and "post only on first open"
Postmark webhook settings: 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 :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s:
rejected, failed, bounced, deferred, autoresponded, opened, complained, unsubscribed, subscribed.
(Postmark does not support sent, delivered, or clicked events.)
The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be
a `dict` of Postmark `bounce <http://developer.postmarkapp.com/developer-bounce-webhook.html>`_
or `open <http://developer.postmarkapp.com/developer-open-webhook.html>`_ webhook data.
.. _Postmark account settings: https://account.postmarkapp.com/servers

View File

@@ -174,3 +174,31 @@ Limitations and quirks
actually OK with that.)
(Tested March, 2016)
.. _sendgrid-webhooks:
Status tracking webhooks
------------------------
If you are using Anymail's normalized :ref:`status tracking <event-tracking>`, enter
the url in your `SendGrid mail settings`_, under "Event Notification":
:samp:`https://{random}:{random}@{yoursite.example.com}/anymail/sendgrid/tracking/`
* *random:random* is an :setting:`ANYMAIL_WEBHOOK_AUTHORIZATION` shared secret
* *yoursite.example.com* is your Django site
Be sure to check the boxes in the SendGrid settings for the event types you want to receive.
SendGrid will report these Anymail :attr:`~anymail.signals.AnymailTrackingEvent.event_type`\s:
queued, rejected, bounced, deferred, delivered, opened, clicked, complained, unsubscribed,
subscribed.
The event's :attr:`~anymail.signals.AnymailTrackingEvent.esp_event` field will be
a `dict` of `Sendgrid event`_ fields, for a single event. (Although SendGrid calls
webhooks with batches of events, Anymail will invoke your signal receiver separately
for each event in the batch.)
.. _SendGrid mail settings: https://app.sendgrid.com/settings/mail_settings
.. _Sendgrid event: https://sendgrid.com/docs/API_Reference/Webhooks/event.html