I'm sending emails via Gmail API and would like to know when the messages bounce. How can I do this?
As I understand it, bounced emails usually contain some sort of header indicating a bounce such as:
X-Failed-Recipients: [email protected]
However, there doesn't seem to always be a header indicating what original messageID it was that bounced.
I was thinking of the following plan, but there are so many holes that I think I must be approaching this wrong.
Problems
A couple other ideas I had were to:
Use the Gmail search tool to search for “from:[email protected]”, and then search. This will show all of the bounce notifications you've received. 2.
Gmail returns a message reflecting the response provided by the recipient's server. Below, find common error messages that you might encounter. Understand why your message bounced and how to fix the problem.
Excluding many free, or bulk email providers, mail servers will usually try for up to 5 days before giving up. After 4 hours, a notice is normally sent back to the sender explaining why there is a delay; at the end of 5 days, a final delivery failure message is sent back to the sender.
When you send a message via
service.users().messages().send(userId, message).execute();
It will return a Message
. You can use its threadId
to check if you got any reply to that message.
Here's an easy way of checking if it bounced (give a 1 second delay after sending):
public static boolean isBounced(Gmail service, String threadId) throws IOException {
List<Message> list = service.users().messages().list("me")
.setQ("[email protected]")
.execute().getMessages();
return list.stream().anyMatch(msg -> msg.getThreadId().equals(threadId));
}
Messages that gets bounced when sent through the Gmail API gets a response from the mailer deamon ([email protected]
). You could continually check the user's messages to see if a new message from the daemon has been received.
Make sure to store the timestamp in seconds since your last check, so you don't get any nasty duplicates next time around.
query = from:[email protected] after:<TIME_SINCE_EPOCH_IN_SECONDS>
GET https://www.googleapis.com/gmail/v1/users/me/messages?q=from%3Amailer-daemon%40googlemail.com+after%3A1437055051&access_token={YOUR_API_KEY}
Response:
{
"messages": [
{
"id": "14e97f7ed03b7e88",
"threadId": "14e97f7ea9b794a4"
},
]
}
I got a bounce! Let's fetch the entire mail and decode it and get the Message-ID you were alluding too.
GET https://www.googleapis.com/gmail/v1/users/me/messages/14e97f7ed03b7e88?fields=payload%2Fbody%2Fdata&access_token={YOUR_API_KEY}
Response:
{
"payload": {
"body": {
"data": "RGVsA0K..."
}
}
}
Converting the mail to regular base64 from its URL safe version (replace all "-" with "+" and "_" with "/"), and base64-decoding it we get:
atob("RGVsA0K...".replace(/\-/g, '+').replace(/\_/g, '/'));
Decoded mail:
"Delivery to the following recipient failed permanently:
[email protected]
Technical details of permanent failure:
DNS Error: Address resolution of sadsads.asdsad. failed: Domain name not found
----- Original message -----
.
.
.
Received: from 292824132082.apps.googleusercontent.com named unknown by
gmailapi.google.com with HTTPREST; Thu, 16 Jul 2015 13:44:43 -0400
from: [email protected]
Date: Thu, 16 Jul 2015 13:44:43 -0400
Message-ID: <[email protected]>
Subject: Subject Text
To: [email protected]
Content-Type: text/plain; charset=UTF-8
The actual message text goes here
Here we have the Message-ID! Let's get the bounced email!
query = rfc822msgid:<[email protected]>;
GET https://www.googleapis.com/gmail/v1/users/me/messages?q=rfc822msgid%3A%3CCADsZLRzOs1wT4B5pgR7oHHdbjkQhuaCQQs8CEckhLwVw73QFEQ%40mail.gmail.com%3E&key={YOUR_API_KEY}
Response:
{
"messages": [
{
"id": "14e97f7ea9b794a4", // <-- Here is the message that bounced!
"threadId": "14e97f7ea9b794a4"
}
],
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With