-
Notifications
You must be signed in to change notification settings - Fork 135
SendGrid smtp-id not always matching anymail_status #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Anymail by default* generates a new message_id for each message it sends through SendGrid. It sets that in the message's Message-ID header, which is what SendGrid is supposed to pass back to webhook calls as the smtp-id field. Two questions:
I'll try to check later to see if SendGrid changed something out from under us, but my best guess right now is they're maybe overriding Message-ID with their own value under some undocumented conditions. * I'm assuming you've already seen Anymail's docs on SendGrid Message-IDs, and that your settings don't override Anymail's default SENDGRID_GENERATE_MESSAGE_ID=True. |
I will relay any useful information I receive from SendGrid support. |
One additional note, the SendGrid engagement events (click and open) have been consistently sending the correct (Anymail) smtp-id. |
OK, I'm able to reproduce. For me, at least, this definitely seems related to whether the From email uses a sender domain that's authenticated with SendGrid (https://app.sendgrid.com/settings/sender_auth). If the From domain is not authenticated, the "smtp-id" sent to the webhooks is different from the Message-ID header in the sent/received message. Details... When I send messages from an authenticated, DKIM-validated domain in my SG account... From: [email protected]
Message-ID: <[email protected]> ... the webhook payload has the correct smtp-id: "smtp-id": "<[email protected]>" BUT when I send messages from @example.com, which is (obviously) not an authenticated domain for my SG account, I see exactly the behavior you described: the received message's Message-ID header matches what Anymail sent... From: [email protected]
Message-ID: <[email protected]> ... but the webhook payloads have a totally different smtp-id: "smtp-id": "<[email protected]>" Please let me know what you hear back from SendGrid support. (I could sort of understand if they were trying to prevent un-authenticated sender domains from appearing anywhere in sent mail headers, but since they're leaving @example.com in the Message-ID and the From header, that's not what's going on.) If anyone from SendGrid engineering stops by here while investigating, the examples above are not redacted; feel free to pull them up in your logs. Also, that incorrect "smtp-id" from the [email protected] webhook payload actually does appear in the message headers, as an "HTTP id": Received: from MjM2NTg4Nw ([REDACTED:sending-machine-ip-address])
by ismtpd0006p1sjc2.sendgrid.net (SG) with HTTP id -g6-N0riQ3WNhvp9aBJpfQ
Thu, 24 May 2018 01:12:16.237 +0000 (UTC) Again, though, it doesn't make any sense that this HTTP id would be substituted for the actual Message-ID value in the webhook 'smtp-id'. |
This is because SendGrid doesn't actually provide smtp-id with the payloads for those events, but Anymail has a trick to work around this using SendGrid's custom_args. |
I've checked and double checked and even confirmed with SendGrid support that our sender validation is setup properly for our sending domain. I discussed the webhook issue at length with SendGrid support yesterday explaining that we see the proper Here's the response I received from SendGrid support:
Following their request I modified the def ensure_message_id(self):
"""Ensure message has a known Message-ID for later event tracking"""
if "Message-ID" not in self.data["headers"]:
# Only make our own if caller hasn't already provided one
self.data["headers"]["Message-ID"] = self.make_message_id()
self.message_id = self.data["headers"]["Message-ID"]
# Workaround for missing message ID (smtp-id) in SendGrid engagement events
# (click and open tracking): because unique_args get merged into the raw event
# record, we can supply the 'smtp-id' field for any events missing it.
self.data.setdefault("custom_args", {})["smtp-id"] = self.message_id
self.data["custom_args"]["anymail-id"] = self.message_id After this update I receive the I'm happy to put together a PR with these changes if you'd like but wanted to follow up with the information I received and a report on what has worked to resolve the issue for my project. |
Thanks for tracking this down and for the PR. Using our own custom_args field is a much better approach than trying to work around SendGrid's unpredictable smtp-id and Message-ID behavior. (Also, past experience indicates we could wait a very long time for SendGrid to fix an acknowledged API bug.) Since the anymail_id is only used for matching up tracking events, I'm sort of inclined to just switch it to something like a uuid, and get rid of all the backend code that mucks with the Message-ID header. We don't generate Message-ID for any other backends. (And with many ESPs, the webhook tracking id is not the Message-ID—in fact, it's often a uuid generated by the ESP.) Any concerns with that? I'm trying to figure out if there's any way this might be a breaking change. (Anymail message_id is supposed to be treated as an opaque string, because there are so many different ESP formats.) |
Definitely support the UUID approach and the simplification it brings. I think it could be a nearly invisible rework as long as we flow the UUID through |
OK, I'm sold. I can either merge your PR and then make the UUID switch separately, or if you're up for it you could update the PR. My memory is the SendGrid v2 backend has exactly the same code (I think it originated pre-v3), and could have the same changes. (This also reminds me it's probably time to deprecate that backend.) |
Happy to make the UUID updates while I'm in there making the updates requested for the PR. |
Fix released in Anymail v3.0. Thanks again for the contribution. |
ESP: SendGrid
django-anymail: 2.2
Python: 2.7
Django: 1.11.10
Just after we make the
msg = AnymailMessage(**kwargs)
call we store the data frommsg.anymail_status
in our database for tracking. The issue we're seeing is that the SendGrid webhook will often times return POST data with amessage_id
andesp_event['smtp-id
] that do not match the values Anymail generated for us.As an example:
We send a message and receive an smtp-id of
<[email protected]>
in themsg.anymail_status
.When the webhook communicates updates to us we find that the
message_id
for the same message is<[email protected]>
which means that we are unable to identify which message this belongs to.Here's the code that calls AnymailMessage:
The
Email()
model holds thecreate_from_anymail(anymail_status, event)
method where event is an optional custom tracker for the event triggering the email send (unrelated to anymail):From what I can gather from the SendGrid webhook documentation the
smtp-id
should be returned for every webhook POST and isa unique ID attached to the message by the originating system
.I've also contacted SendGrid support but wanted to present the issue here in case anyone has seen something similar and may have ideas on how to get a consistent identifier from our system into SendGrid.
The text was updated successfully, but these errors were encountered: