How to avoid loading attachments data from Gmail Rest API get message - gmail-api

How to avoid loading attachments data from Gmail Rest API with get message request.
Using the fields parameter at least we can avoid loading few fields but when I want to load message body, attachment data also coming along with body just like IMAP

You don't get the attachment like you do in IMAP. You get an attachmentId which you have to use in an additional request to get the attachment.
I just sent a message with an attached image to myself. This is what the response from the API looks like:
{
"id": "1573ec1aa0976b42",
"threadId": "1573ec1aa0976b42",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT",
"UNREAD"
],
"snippet": "",
"historyId": "939514",
"internalDate": "1474226662000",
"payload": {
"mimeType": "multipart/related",
"filename": "",
"headers": [ ... ],
"body": {
"size": 0
},
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=94eb2c0d3cba8637a2053ccd2461"
}
],
"body": {
"size": 0
},
"parts": [
{ ... },
{
"partId": "1",
"mimeType": "image/png",
"filename": "Screen Shot",
"headers": [ ... ],
"body": {
"attachmentId": "ANGjdJ-bmCvsIaV-4KfALXzVV_D567w4i6ksLnwIZhLAl3VXCE335663UbmOLC_vbLrCFusNtnWVpdTv3i88uR482kFwLZqAcwmI7C5gFlamob2aK4-lqAPlCZs17jtCQR9y5Mt4nnpP_Kg64N9qgXbDF0E2vYnEw4xwtEKEo4fRIAbc94ZjjfynFD832mh1B37XFMt-bYw9wkNv24xBife0koBNYpKs-gGLJkfu2EoZouqunGDX9ry1jq2jW2AClWcFXPXvgRBMjUcoRDPtvb9LLLrBhDjU1hu6r1Ibc3c2BSoBogT8QyIp2VUCuFU",
"size": 1511996
}
}
]
},
"sizeEstimate": 1513185
}
As you can see, it just contains an attachmentId and no actual attachment data.

Related

Gmail API: how to fetch message body without attachments

According to https://developers.google.com/gmail/api/v1/reference/users/messages/get
it seems choosing either 'full' and 'raw' formats are the options to obtain an email's body.
Is there a way to fetch email body, but not attachments?
The format raw will give you everything in the mail (attachments included). The format full will however give you ids for attachments instead of the data, so full is the format you want to use.
Here is an example of how to get the body from a full format response:
var response = {
"payload": {
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
}
],
"body": {
"size": 9,
"data": "V293IG1hbg0K"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
}
],
"body": {
"size": 30,
"data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "feelthebern.jpg",
"headers": [
{
"name": "Content-Type",
"value": "image/jpeg; name=\"feelthebern.jpg\""
},
{
"name": "Content-Disposition",
"value": "attachment; filename=\"feelthebern.jpg\""
},
{
"name": "Content-Transfer-Encoding",
"value": "base64"
},
{
"name": "X-Attachment-Id",
"value": "f_ieq3ev0i0"
}
],
"body": {
"attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
"size": 100446
}
}
]
}
};
function decode(string) {
return decodeURIComponent(escape(atob(string.replace(/\-/g, '+').replace(/\_/g, '/'))));
}
function getText(response) {
var result = '';
// In e.g. a plain text message, the payload is the only part.
var parts = [response.payload];
while (parts.length) {
var part = parts.shift();
if (part.parts) {
parts = parts.concat(part.parts);
}
if (part.mimeType === 'text/plain') {
// Continue to look for a 'text/html' part.
result = decode(part.body.data);
} else if (part.mimeType === 'text/html') {
// 'text/html' part found. No need to continue.
result = decode(part.body.data);
break;
}
}
return result;
}
var text = getText(response);
console.log(text);

Different response on similar requests Gmail API

I'm trying to handle the response object from Gmail API with getting attachments from certain messages:
In my Gmail account i have 2 Labels:
Label_1
Label_2
both messages get daily reports from different web services and im using the Gmail API to first get a list of messages while im filtering a message with a Label and a query search string (q field) which contains a certain date (this is how i get only one message when i request a list)
with the response object Im able to get the message ID and then send another request with the message ID.
I want to download the attachments so in the response object im searching for the Payload.Parts field of the response object
In Label_1 response - Payload.Parts[0] contains the filename field and Payload.Parts[0].Body contains the attachmentID
In Label_2 response - Payload.Parts[1].Parts[0] contains the filename and same for .Body contains the attachmentID.
my question is: why this is happening?
why in one response i get the wanted fields in the first Payload.Parts and in the second i have to go deeper in the object's fields?
I've also noticed that in Label_1 response I receive an HTTP header which I dont get on the second one that I think may be related: DKIM-Signature
any thoughts?
Thanks.
The response you get is just the RFC822-message parsed to JSON. The message might be multipart/mixed, multipart/related, text/html or something similar. It is best to write your code so you check all parts in the payload:
var response = {
"payload": {
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
}
],
"body": {
"size": 9,
"data": "V293IG1hbg0K"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
}
],
"body": {
"size": 30,
"data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "feelthebern.jpg",
"headers": [
{
"name": "Content-Type",
"value": "image/jpeg; name=\"feelthebern.jpg\""
},
{
"name": "Content-Disposition",
"value": "attachment; filename=\"feelthebern.jpg\""
},
{
"name": "Content-Transfer-Encoding",
"value": "base64"
},
{
"name": "X-Attachment-Id",
"value": "f_ieq3ev0i0"
}
],
"body": {
"attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
"size": 100446
}
}
]
}
};
// In e.g. a plain text message, the payload is the only part.
var parts = [response.payload];
var attachmentIds = [];
while (parts.length) {
var part = parts.shift();
if (part.parts) {
parts = parts.concat(part.parts);
}
if(part.body && part.body.attachmentId) {
attachmentIds.push(part.body.attachmentId);
}
}
console.log(attachmentIds);

Grabbing message body with Gmail API

Whenever I make a test request via API explorer (https://developers.google.com/apis-explorer/#p/gmail/v1/gmail.users.messages.get?) I get a payload that contains numerous body tags in the JSON output. I need to grab the body tag that represents the text of the message body and nothing else. How do I know, in each response, which body tag that is?
You can check the mimeType of the parts in the payload for a part with type text/html or text/plain:
var response = {
"payload": {
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
}
],
"body": {
"size": 9,
"data": "V293IG1hbg0K"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
}
],
"body": {
"size": 30,
"data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "feelthebern.jpg",
"headers": [
{
"name": "Content-Type",
"value": "image/jpeg; name=\"feelthebern.jpg\""
},
{
"name": "Content-Disposition",
"value": "attachment; filename=\"feelthebern.jpg\""
},
{
"name": "Content-Transfer-Encoding",
"value": "base64"
},
{
"name": "X-Attachment-Id",
"value": "f_ieq3ev0i0"
}
],
"body": {
"attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
"size": 100446
}
}
]
}
};
function decode(string) {
return decodeURIComponent(escape(atob(string.replace(/\-/g, '+').replace(/\_/g, '/'))));
}
function getText(response) {
var result = '';
// In e.g. a plain text message, the payload is the only part.
var parts = [response.payload];
while (parts.length) {
var part = parts.shift();
if (part.parts) {
parts = parts.concat(part.parts);
}
if (part.mimeType === 'text/plain') {
// Continue to look for a 'text/html' part.
result = decode(part.body.data);
} else if (part.mimeType === 'text/html') {
// 'text/html' part found. No need to continue.
result = decode(part.body.data);
break;
}
}
return result;
}
var text = getText(response);
console.log(text);
They all are. A MIME multipart message contains multiple bodies. how to convert it to what you consider a "standard" representation depends. Sometimes the parts are alternatives (i. e. HTML vs. txt) and you can pick one. It could also be a file attachment, a signature, etc.
See https://en.wikipedia.org/wiki/MIME#Multipart_messages for details on the format.

Where to find body of email depending of mimeType

I am making a request to the User.messages endpoint. All objects returned (the emails) have a mimeType property which I'm struggling to understand.
More specifically, I want to be able to extract the body of the email depending of the mimeType since I've been able to notice that depending on the mimeType, the body will be inside the body property in payload, or in the parts array. What are the different mimeTypes that can be returned, and where can I find the body of the email for each one of them?
I think it will make sense if you think of the payload as a part in of itself. Let's say I send a message with just a subject and a plain message text:
From: emtholin#gmail.com
To: emtholin#gmail.com
Subject: Example Subject
This is the plain text message
This will result in the following parsed message:
{
"id": "154ecb53c10b74d8",
"threadId": "154ecb53c10b74d8",
"labelIds": [
"INBOX",
"SENT"
],
"snippet": "This is the plain text message",
"historyId": "38877",
"internalDate": "1464260181000",
"payload": {
"partId": "",
"mimeType": "text/plain",
"filename": "",
"headers": [
...
],
"body": {
"size": 31,
"data": "VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlCg=="
}
},
"sizeEstimate": 355
}
If I send a message with a plain text part, a html part and an image, it will look like this when parsed:
{
"id": "154ed5ccaa12f3df",
"threadId": "154ed5ccaa12f3df",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT"
],
"snippet": "This is a plain/html message with an image.",
"historyId": "841379",
"internalDate": "1464271162000",
"payload": {
"mimeType": "multipart/mixed",
"filename": "",
"headers": [
...
],
"body": {
"size": 0
},
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=089e0122896c7c80d80533bf3205"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
}
],
"body": {
"size": 47,
"data": "VGhpcyBpcyBhIHBsYWluL2h0bWwgKm1lc3NhZ2UqIHdpdGggYW4gaW1hZ2UuDQo="
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
}
],
"body": {
"size": 73,
"data": "PGRpdiBkaXI9Imx0ciI-VGhpcyBpcyBhIHBsYWluL2h0bWwgPGI-bWVzc2FnZTwvYj4gd2l0aCBhbiBpbWFnZS48L2Rpdj4NCg=="
}
}
]
},
{
"partId": "1",
"mimeType": "image/png",
"filename": "smile.png",
"headers": [
...
],
"body": {
"attachmentId": "ANGjdJ-OrSy7VAYL-UbRyNtmySbZLlV-fV43zJF0_neNGZ8yKugsZAxb32eSb-CrbYIhF9NvjGwBVEjSkRrUWoCS7aDpgoQnt9WR7f2sa17qVEyOg_JVSbrGrunirvQw2dY-SxxB3Y0JP3aYDHSBXpNO6fFCByVFWQDw1et5Mh9di7bGO4AWOLKFVe_Yb2RmdDwuazGXGb8zA88TTMaiEPIacPTNiVtBrIWG0EKGxHBhep9j8ujyWeCS5P9X80dBHvBNj4T9XjUwcrN6FvwegRewRMM9cBupY7jQESR7915OcbhCNyi5l64x6vVh1ZU",
"size": 2002
}
}
]
},
"sizeEstimate": 3077
}
You will see it's just the RFC822-message parsed to JSON. If you just traverse the parts, and treat the payload as a part itself, you will find what you are looking for.
var parts = [response.payload];
while (parts.length) {
var part = parts.shift();
if (part.parts) {
parts = parts.concat(part.parts);
}
if(part.mimeType === 'text/html') {
var decodedPart = decodeURIComponent(escape(atob(part.body.data.replace(/\-/g, '+').replace(/\_/g, '/'))));
console.log(decodedPart);
}
}
There are many MIME types that can be returned, here are a few:
text/plain: the message body only in plain text
text/html: the message body only in HTML
multipart/alternative: will contain two parts that are alternatives for each othe, for example:
a text/plain part for the message body in plain text
a text/html part for the message body in html
multipart/mixed: will contain many unrelated parts which can be:
multipart/alternative as above, or text/plain or text/html as above
application/octet-stream, or other application/* for application specific mime types for attachments
image/png ot other image/* for images, which could be embedded in the message.
The definitive reference for all this is RFC 2046 https://www.ietf.org/rfc/rfc2046.txt (you might want to also see 2044 and 2045)
To answer your question, build a tree of the message, and look either for:
the first text/plain or text/html part (either in the message body or in a multipart/mixed)
the first text/plain or text/html inside of a multipart/alternative, which may be part of a multipart mixed.
An example of a complex message:
multipart/mixed
multipart/alternative
text/plain <- message body in plain text
text/html <- message body in HTML
application/zip <- a zip file attachment
-
I know this question is not new but I've wrote a PHP script which correctly parses messages pulled from Gmail API, including any type of attachment.
The script includes a recursive "iterateParts" function which iterates all message parts so we can be sure we extracted all available data from each message.
Script steps are:
Pull all message ids from API
Get some important headers (subject & from address)
Either body is directly on payload or send payload to iterateParts
iterateParts is parsing each message to $msgArr with it's data, base64 encoded
Push $msgArr to master array $allmsgArr
Traverse master array and save each part as file according to it's MIME type and filename
$maxToPull = 1;
$gmailQuery = "ALL";
// Initializing Google API
$service = new Google_Service_Gmail($client);
// Pulling all gmail messages into $messages array
$user = 'me';
$msglist = $service->users_messages->listUsersMessages($user, ["maxResults"=>$maxToPull, "q"=>$gmailQuery]);
$messages = $msglist->getMessages();
// Master array that will hold all parsed messages data, including attachments
$allmsgArr = array();
// Traverse each message
foreach($messages as $message)
{
$msgArr = array();
$single_message = $service->users_messages->get('me', $message->getId());
$payload = $single_message->getPayload();
// Nice to have the gmail msg id, can be used to direct access the message in Gmail's web gui
$msgArr['gmailmsgid'] = $message->getId();
// Retrieving the subject and "from" email address
foreach($payload->getheaders() as $oneheader)
{
if($oneheader['name'] == 'Subject')
$msgArr['subject'] = $oneheader['value'];
if($oneheader['name'] == 'From')
$msgArr['fromaddress'] = substr($oneheader['value'], strpos($oneheader['value'], '<')+1, -1);
}
// If body is directly in the message payload (only for plain text messages where there's no HTML part and no attachments, normally this is not the case)
if($payload['body']['size'] > 0)
$msgArr['textplain'] = $payload['body']['data'];
// Else, iterate over each message part and continue to dig if necessary
else
iterateParts($payload, $message->getId());
// Push the parsed $msgArr (parsed by iterateParts) to master array
array_push($allmsgArr, $msgArr);
}
// Traverse each parsed message and saving it's content and attachments to files
foreach($allmsgArr as $onemsgArr)
{
$folder = "messages/".$onemsgArr['gmailmsgid'];
mkdir($folder);
if($onemsgArr['textplain'])
file_put_contents($folder."/textplain.txt", decodeData($onemsgArr['textplain']));
if($onemsgArr['texthtml'])
file_put_contents($folder."/texthtml.html", decodeData($onemsgArr['texthtml']));
if($onemsgArr['attachments'])
{
foreach($onemsgArr['attachments'] as $oneattachment)
{
if(!empty($oneattachment['filename']))
$filename = $oneattachment['filename'];
else if($oneattachment['mimetype'] == "message/rfc822" && empty($oneattachment['filename'])) // email attachments
$filename = "noname.eml";
else
$filename = "unknown";
file_put_contents($folder."/".$filename, decodeData($oneattachment['data']));
}
}
}
function iterateParts($obj, $msgid) {
global $msgArr;
global $service;
foreach($obj as $parts)
{
// if found body data
if($parts['body']['size'] > 0)
{
// plain text representation of message body
if($parts['mimeType'] == 'text/plain')
{
$msgArr['textplain'] = $parts['body']['data'];
}
// html representation of message body
else if($parts['mimeType'] == 'text/html')
{
$msgArr['texthtml'] = $parts['body']['data'];
}
// if it's an attachment
else if(!empty($parts['body']['attachmentId']))
{
$attachArr['mimetype'] = $parts['mimeType'];
$attachArr['filename'] = $parts['filename'];
$attachArr['attachmentId'] = $parts['body']['attachmentId'];
// the message holds the attachment id, retrieve it's data from users_messages_attachments
$attachmentId_base64 = $parts['body']['attachmentId'];
$single_attachment = $service->users_messages_attachments->get('me', $msgid, $attachmentId_base64);
$attachArr['data'] = $single_attachment->getData();
$msgArr['attachments'][] = $attachArr;
}
}
// if there are other parts inside, go get them
if(!empty($parts['parts']) && !empty($parts['mimeType']) && empty($parts['body']['attachmentId']))
{
iterateParts($parts->getParts(), $msgid);
}
}
}
// All data returned from API is base64 encoded
function decodeData($data)
{
$sanitizedData = strtr($data,'-_', '+/');
return base64_decode($sanitizedData);
}
This is how $allmsgArr will look like (where only one message was pulled):
Array
(
[0] => Array
(
[gmailmsgid] => 25k1asfa556x2da
[fromaddress] => john#gmail.com
[subject] => Fwd: Sea gulls picture
[textplain] => UE5SIDQxQzAwMg0KDQpBUkJFTFRFU1QxDQoNCg0K
[texthtml] => PGRpdiBkaXI9Imx0ciI-PHNwYW4gc3R5bGU9ImZi
[attachments] => Array
(
[0] => Array
(
[mimetype] => image/png
[filename] => sea_gulls.png
[attachmentId] => ANGjdJ9tmy4d8vPXhU_BjNEFEaDODOpu29W2u5OTM7a0
[data] => iVBORw0KGgoAAAANSUhEUgAABSYAAAKWCAYAAABUP
)
[1] => Array
(
[mimetype] => image/jpeg
[filename] => Outlook_Signature.jpg
[attachmentId] => ANGjdJ-CgZTK0oK44Q8j7TlN_JlaexxGKZ_wHFfoEB
[data] => 6jRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEa
)
)
)
)
Based on the Tholle idea, I've completed his script to extract Gmail body and attachments.
First of all, you should fetch any gmail-message object and then parse it.
You can fetch any gmail-message with this code:
const {google} = require('googleapis')
// do your authenticatoin here
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectTo)
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
const response = await this.gmail.users.messages.get({
auth: oAuth2Client,
userId: 'me',
id: messageId,
format: 'full'
})
const message_obj = response.data
Main Script:
function parser(response) {
function decode(input) {
const text = new Buffer.from(input, 'base64').toString('ascii')
return decodeURIComponent(escape(text))
}
function decode_alternative(input) {
// this way does not escape special "B" characters
// const text = Buffer.from(input, 'base64').toString('ascii')
// return decodeURIComponent(escape(text))
return base64.decode(input.replace(/-/g, '+').replace(/_/g, '/'))
}
const result = {
text: '',
html: '',
attachments: []
}
let parts = [response.payload]
while (parts.length) {
let part = parts.shift()
if (part.parts)
parts = parts.concat(part.parts)
if (part.mimeType === 'text/plain')
result.text = decode(part.body.data)
if (part.mimeType === 'text/html')
result.html = decode(part.body.data)
if (part.body.attachmentId) {
result.attachments.push({
'partId': part.partId,
'mimeType': part.mimeType,
'filename': part.filename,
'body': part.body
})
}
}
return result
}
Sample Data and response:
const with_multi_type_attachments = {
"id": "16c624e85dfd9883",
"threadId": "16c62397458f34b1",
"labelIds": [],
"snippet": "This is body. Inline-attachments my-custom-link my-custom-email-address Emoji: 😂😎 closure.mp4",
"historyId": "14006828",
"internalDate": "1565017407000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 261,
"data": "VGhpcyBpcyBib2R5Lg0KDQpJbmxpbmUtYXR0YWNobWVudHMNCltpbWFnZTogMTMuanBnXQ0KbXktY3VzdG9tLWxpbmsgPGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS8-DQpteS1jdXN0b20tZW1haWwtYWRkcmVzcyA8bWVAd29yay5jb20-DQoNCkVtb2ppOg0K8J-YgvCfmI4NCg0KIGNsb3N1cmUubXA0DQo8aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xVXo4cHpoamh4UC13b0c5dFFkaGM2R1h2RGwzYUpSS3Uvdmlldz91c3A9ZHJpdmVfd2ViPg0K"
}
},
{
"partId": "0.0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 1244,
"data": "PGRpdiBkaXI9Imx0ciI-PGJyPjxkaXYgY2xhc3M9ImdtYWlsX3F1b3RlIj48ZGl2IGRpcj0ibHRyIj48ZGl2PlRoaXMgaXMgYm9keS48L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2PklubGluZS1hdHRhY2htZW50czxicj48L2Rpdj48ZGl2PjxkaXY-PGltZyBzcmM9ImNpZDppaV9qeXlpNjgyNjAiIGFsdD0iMTMuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OjBweCIgd2lkdGg9IjIyNSIgaGVpZ2h0PSIyMjUiPjxicj48YSBocmVmPSJodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vIiB0YXJnZXQ9Il9ibGFuayI-bXktY3VzdG9tLWxpbms8L2E-PGEgaHJlZj0ibWFpbHRvOm1lQHdvcmsuY29tIiB0YXJnZXQ9Il9ibGFuayI-PGJyPm15LWN1c3RvbS1lbWFpbC1hZGRyZXNzPC9hPjwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-RW1vamk6PGJyPjwvZGl2PjxkaXY-8J-YgvCfmI48L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2IGNsYXNzPSJnbWFpbF9jaGlwIGdtYWlsX2RyaXZlX2NoaXAiIHN0eWxlPSJ3aWR0aDozOTZweDtoZWlnaHQ6MThweDttYXgtaGVpZ2h0OjE4cHg7YmFja2dyb3VuZC1jb2xvcjojZjVmNWY1O3BhZGRpbmc6NXB4O2NvbG9yOiMyMjI7Zm9udC1mYW1pbHk6YXJpYWw7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXNpemU6MTNweDtib3JkZXI6MXB4IHNvbGlkICNkZGQ7bGluZS1oZWlnaHQ6MSIgY29udGVudGVkaXRhYmxlPSJmYWxzZSI-PGEgaHJlZj0iaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xVXo4cHpoamh4UC13b0c5dFFkaGM2R1h2RGwzYUpSS3Uvdmlldz91c3A9ZHJpdmVfd2ViIiB0YXJnZXQ9Il9ibGFuayIgc3R5bGU9ImRpc3BsYXk6aW5saW5lLWJsb2NrO21heC13aWR0aDozNjZweDtvdmVyZmxvdzpoaWRkZW47dGV4dC1vdmVyZmxvdzplbGxpcHNpczt3aGl0ZS1zcGFjZTpub3dyYXA7dGV4dC1kZWNvcmF0aW9uOm5vbmU7cGFkZGluZzoxcHggMDtib3JkZXI6bm9uZSI-PGltZyBzdHlsZT0idmVydGljYWwtYWxpZ246IGJvdHRvbTsgYm9yZGVyOiBub25lOyIgc3JjPSJodHRwczovL3NzbC5nc3RhdGljLmNvbS9kb2NzL2RvY2xpc3QvaW1hZ2VzL2ljb25fMTBfZ2VuZXJpY19saXN0LnBuZyI-wqA8c3BhbiBkaXI9Imx0ciIgc3R5bGU9ImNvbG9yOiMxNWM7dGV4dC1kZWNvcmF0aW9uOm5vbmU7dmVydGljYWwtYWxpZ246Ym90dG9tIj5jbG9zdXJlLm1wNDwvc3Bhbj48L2E-PC9kaXY-PC9kaXY-PC9kaXY-PC9kaXY-PC9kaXY-DQo="
}
}
]
},
{
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "13.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-BkrF-Gf3L_44M9RIu8clbEbFwu0xLlfL3YOEja7y5pxZecT7YfI6hnI6-PvJ9G-w6orcWYN9jgYAOsHBCBFHezwihjdVWKBYkGB9gmdCBFVK0XKHhGODyMPJLTW2kbbhyTLFBDjEo33Tld7XMtbRAvULGV_Z6mue8XdNAfxcAnhNOGZ48Pi7y_IugKIIXZ_DAD3JvALMxctRONavk7_-oOtf09ksIQPshaA",
"size": 58159
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attachment-1.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ_M5qM10ANxuQiqn7xLsqGiB01ZfnkpzDd186JRI7NxB7l9nA2xN3EmhKygGyG4aSWeHiZeo-R9NXDG-HKWIDnuOwLoAiLLggvsQ5qlXwsGhKS7f383YaVJs8joI1Q5JtsepWPkBaBCR2wPviMt4mX_L5M7Em8GzKVtLh7fuPFbXguaHIUoyhCXP6mnKXQmNiyaatPvlB7_KPgi7h5wD9gDctpPSu59mUs-9Q",
"size": 707870
}
},
{
"partId": "2",
"mimeType": "video/mp4",
"filename": "closure.mp4",
"headers": [],
"body": {
"attachmentId": "ANGjdJ9iIPaAkkhk7mNFzOU5lQuNMRmiMxgZVY1NDsO-p6XvvbyoEqNgRqz4pepUK5HcGpOGJRMHB0ec9_wlHYbNfhr6aPvdIRO-VD4-Baw55yHgZ8KWhHAiZ3l-BY5nTB6B1xpRwEqKoun52EkPDCRf8g87tEOtq7p4ut02hg",
"size": 7261025
}
}
]
},
"sizeEstimate": 10987809
}
const with_attachmetnst = {
"id": "16c54135a9d42ab4",
"threadId": "16c5411a8c7fcaa8",
"labelIds": [],
"snippet": "With-Attachment-Body droped-ing.jpg",
"historyId": "14005159",
"internalDate": "1564778649000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 75,
"data": "V2l0aC1BdHRhY2htZW50LUJvZHkNCg0KDQpbaW1hZ2U6IGRyb3BlZC1pbmcuanBnXSA8aHR0cDovL2Ryb3BlZC1pbmcuanBnPg0K"
}
},
{
"partId": "0.0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 247,
"data": "PGRpdiBkaXI9Imx0ciI-PGRpdj5XaXRoLUF0dGFjaG1lbnQtQm9keTwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-PGRpdj48YSBocmVmPSJodHRwOi8vZHJvcGVkLWluZy5qcGciPjxpbWcgc3JjPSJjaWQ6aWlfanl1a3M4NnQwIiBhbHQ9ImRyb3BlZC1pbmcuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OiAwcHg7IiB3aWR0aD0iMjIzIiBoZWlnaHQ9IjIyMyI-PC9hPjxicj48L2Rpdj48L2Rpdj48L2Rpdj4NCg=="
}
}
]
},
{
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "droped-ing.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-hNpJEK_gzrYEsdQODp-Zwe5QoJP5ONsOy5JoSUC1Qk5Po7KgL_AJnMddPRZ1GWpltr-XRtXv3zS5TTUYJxf7BBZyitkMNH9Kv_rnArqXLJyBOqfL1wNqAJPeQrCzUjk6d0ahqAl6ixNyCCgTu-fxvngaBllXU5pTI3_iL6jWXoin6LoQ-a32vhKs319tChOz5GBuTCTov9oZqTtJPvj1yIqLAmUf8vochDQ",
"size": 43716
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attached-img.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-wHL_YHq_zUYZ4AyCHdstehG_7lhh21SXnzvf_33oECSiFcua3UTEbO2u5gSrEVDS4xvdnQa4e2JFb5olkMbv8rBuAprlADc_99pG_X-kf9gjhCiEIPPpr66S7VkB2Wumh9rBFc0bN6j_8mEjoGEBDAyd7lb38SiY8A6v2TP2o9gaKucYfIB__tiQ4Z1C-pSipyNmToCJfE87TuFp_ukQtDQbrVyG1bEoy2w",
"size": 44988
}
},
{
"partId": "2",
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"filename": "Motivation Letter Sample.docx",
"headers": [],
"body": {
"attachmentId": "ANGjdJ9c8IKsgC8Do8iAyDHHGiyiho_zv8eI4BkaW_kvnl7--webl9P01xGWTyCwTRblgClK6dE2JH8P7hNd007ZK-CIU5Stwuc6Mp2v-KHC3slmCfko--JRrU1-EotFJqrnr3gmRw9qZgWlCqKxqiJAo1afE67LtwBSuR_frjCCUPH7RuxdY6mP_WJHSP6XA6i5uyhWaRGpnMOzawbTdq1_ZSKo9mjF__dDOsdrlQ",
"size": 7136
}
}
]
},
"sizeEstimate": 133087
}
const without_attachmetnst = {
"id": "16c579bc78fc11b1",
"threadId": "16c579b9b32bc8c5",
"labelIds": [],
"snippet": "Its Body",
"historyId": "14005084",
"internalDate": "1564837921000",
"payload": {
"partId": "",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 10,
"data": "SXRzIEJvZHkNCg=="
}
},
{
"partId": "1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 47,
"data": "PGRpdiBkaXI9Imx0ciI-SXRzIEJvZHk8YnIgY2xlYXI9ImFsbCI-PC9kaXY-DQo="
}
}
]
},
"sizeEstimate": 584
}
const only_inline_attachment = {
"id": "16c61fb94d1b287f",
"threadId": "16c60a9c8f51833b",
"labelIds": [],
"snippet": "Just-has-inline-attachments-Body",
"historyId": "14005012",
"internalDate": "1565011972000",
"payload": {
"partId": "",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 78,
"data": "SnVzdC1oYXMtaW5saW5lLWF0dGFjaG1lbnRzLUJvZHkNCg0KDQpbaW1hZ2U6IEJyb256ZS1GbG9vci1MYW1wLTYwMHg2MDAuanBnXQ0K"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 294,
"data": "PGRpdiBkaXI9Imx0ciI-PGJyPjxkaXYgY2xhc3M9ImdtYWlsX3F1b3RlIj48ZGl2IGRpcj0ibHRyIj48ZGl2Pkp1c3QtaGFzLWlubGluZS1hdHRhY2htZW50cy1Cb2R5PC9kaXY-PGRpdj48YnI-PC9kaXY-PGRpdj48ZGl2Pjxicj48ZGl2PjxpbWcgc3JjPSJjaWQ6aWlfanl5MmtjN2IwIiBhbHQ9IkJyb256ZS1GbG9vci1MYW1wLTYwMHg2MDAuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OjBweCIgd2lkdGg9IjIyNSIgaGVpZ2h0PSIyMjUiPjxicj48L2Rpdj48L2Rpdj48L2Rpdj48L2Rpdj4NCjwvZGl2PjwvZGl2Pg0K"
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "Bronze-Floor-Lamp-600x600.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-DX3652q5kW-6nNaEQL-q2zvx1Df2FV-GOfx7YfzcY2NnVkM4uMmr058QUaiAX4wsI3LybFtMB6Xaqy6ijsx2RAUg56nESumo2ecDvs-3PUgDshHJgHluwcEmpDIh7H9w6TEHteDDAs9v4jcu5xX-2_GNyeKUVK8BfGY-qYvgwVwWL5fg-TaiZ6SyrtB_w8dSOpQwtw_at25oeGRpmMh7qkLrbZ6yAkfMIXw",
"size": 91648
}
}
]
},
"sizeEstimate": 126975
}
parser(with_multi_type_attachments)
parser(with_attachmetnst)
parser(without_attachmetnst)
parser(only_inline_attachment)
Result
with_multi_type_attachments: {
"text": "This is body.\r\n\r\nInline-attachments\r\n[image: 13.jpg]\r\nmy-custom-link <http://stackoverflow.com/>\r\nmy-custom-email-address <me#work.com>\r\n\r\nEmoji:\r\np\u001f\u0018\u0002p\u001f\u0018\u000e\r\n\r\n closure.mp4\r\n<https://drive.google.com/file/d/1Uz8pzhjhxP-woG9tQdhc6GXvDl3aJRKu/view?usp=drive_web>\r\n",
"html": "<div dir=\"ltr\"><br><div class=\"gmail_quote\"><div dir=\"ltr\"><div>This is body.</div><div><br></div><div>Inline-attachments<br></div><div><div><img src=\"cid:ii_jyyi68260\" alt=\"13.jpg\" style=\"margin-right:0px\" width=\"225\" height=\"225\"><br>my-custom-link<br>my-custom-email-address</div><div><br></div><div>Emoji:<br></div><div>p\u001f\u0018\u0002p\u001f\u0018\u000e</div><div><br></div><div class=\"gmail_chip gmail_drive_chip\" style=\"width:396px;height:18px;max-height:18px;background-color:#f5f5f5;padding:5px;color:#222;font-family:arial;font-style:normal;font-weight:bold;font-size:13px;border:1px solid #ddd;line-height:1\" contenteditable=\"false\"><img style=\"vertical-align: bottom; border: none;\" src=\"https://ssl.gstatic.com/docs/doclist/images/icon_10_generic_list.png\">B <span dir=\"ltr\" style=\"color:#15c;text-decoration:none;vertical-align:bottom\">closure.mp4</span></div></div></div></div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attachment-1.jpg",
"body": {
"attachmentId": "ANGjdJ_M5qM10ANxuQiqn7xLsqGiB01ZfnkpzDd186JRI7NxB7l9nA2xN3EmhKygGyG4aSWeHiZeo-R9NXDG-HKWIDnuOwLoAiLLggvsQ5qlXwsGhKS7f383YaVJs8joI1Q5JtsepWPkBaBCR2wPviMt4mX_L5M7Em8GzKVtLh7fuPFbXguaHIUoyhCXP6mnKXQmNiyaatPvlB7_KPgi7h5wD9gDctpPSu59mUs-9Q",
"size": 707870
}
}, {
"partId": "2",
"mimeType": "video/mp4",
"filename": "closure.mp4",
"body": {
"attachmentId": "ANGjdJ9iIPaAkkhk7mNFzOU5lQuNMRmiMxgZVY1NDsO-p6XvvbyoEqNgRqz4pepUK5HcGpOGJRMHB0ec9_wlHYbNfhr6aPvdIRO-VD4-Baw55yHgZ8KWhHAiZ3l-BY5nTB6B1xpRwEqKoun52EkPDCRf8g87tEOtq7p4ut02hg",
"size": 7261025
}
}, {
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "13.jpg",
"body": {
"attachmentId": "ANGjdJ-BkrF-Gf3L_44M9RIu8clbEbFwu0xLlfL3YOEja7y5pxZecT7YfI6hnI6-PvJ9G-w6orcWYN9jgYAOsHBCBFHezwihjdVWKBYkGB9gmdCBFVK0XKHhGODyMPJLTW2kbbhyTLFBDjEo33Tld7XMtbRAvULGV_Z6mue8XdNAfxcAnhNOGZ48Pi7y_IugKIIXZ_DAD3JvALMxctRONavk7_-oOtf09ksIQPshaA",
"size": 58159
}
}]
}
with_attachmetnst: {
"text": "With-Attachment-Body\r\n\r\n\r\n[image: droped-ing.jpg] <http://droped-ing.jpg>\r\n",
"html": "<div dir=\"ltr\"><div>With-Attachment-Body</div><div><br></div><div><br></div><div><div><img src=\"cid:ii_jyuks86t0\" alt=\"droped-ing.jpg\" style=\"margin-right: 0px;\" width=\"223\" height=\"223\"><br></div></div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attached-img.jpg",
"body": {
"attachmentId": "ANGjdJ-wHL_YHq_zUYZ4AyCHdstehG_7lhh21SXnzvf_33oECSiFcua3UTEbO2u5gSrEVDS4xvdnQa4e2JFb5olkMbv8rBuAprlADc_99pG_X-kf9gjhCiEIPPpr66S7VkB2Wumh9rBFc0bN6j_8mEjoGEBDAyd7lb38SiY8A6v2TP2o9gaKucYfIB__tiQ4Z1C-pSipyNmToCJfE87TuFp_ukQtDQbrVyG1bEoy2w",
"size": 44988
}
}, {
"partId": "2",
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"filename": "Motivation Letter Sample.docx",
"body": {
"attachmentId": "ANGjdJ9c8IKsgC8Do8iAyDHHGiyiho_zv8eI4BkaW_kvnl7--webl9P01xGWTyCwTRblgClK6dE2JH8P7hNd007ZK-CIU5Stwuc6Mp2v-KHC3slmCfko--JRrU1-EotFJqrnr3gmRw9qZgWlCqKxqiJAo1afE67LtwBSuR_frjCCUPH7RuxdY6mP_WJHSP6XA6i5uyhWaRGpnMOzawbTdq1_ZSKo9mjF__dDOsdrlQ",
"size": 7136
}
}, {
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "droped-ing.jpg",
"body": {
"attachmentId": "ANGjdJ-hNpJEK_gzrYEsdQODp-Zwe5QoJP5ONsOy5JoSUC1Qk5Po7KgL_AJnMddPRZ1GWpltr-XRtXv3zS5TTUYJxf7BBZyitkMNH9Kv_rnArqXLJyBOqfL1wNqAJPeQrCzUjk6d0ahqAl6ixNyCCgTu-fxvngaBllXU5pTI3_iL6jWXoin6LoQ-a32vhKs319tChOz5GBuTCTov9oZqTtJPvj1yIqLAmUf8vochDQ",
"size": 43716
}
}]
}
without_attachmetnst: {
"text": "Its Body\r\n",
"html": "<div dir=\"ltr\">Its Body<br clear=\"all\"></div>\r\n",
"attachments": []
}
only_inline_attachment: {
"text": "Just-has-inline-attachments-Body\r\n\r\n\r\n[image: Bronze-Floor-Lamp-600x600.jpg]\r\n",
"html": "<div dir=\"ltr\"><br><div class=\"gmail_quote\"><div dir=\"ltr\"><div>Just-has-inline-attachments-Body</div><div><br></div><div><div><br><div><img src=\"cid:ii_jyy2kc7b0\" alt=\"Bronze-Floor-Lamp-600x600.jpg\" style=\"margin-right:0px\" width=\"225\" height=\"225\"><br></div></div></div></div>\r\n</div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "Bronze-Floor-Lamp-600x600.jpg",
"body": {
"attachmentId": "ANGjdJ-DX3652q5kW-6nNaEQL-q2zvx1Df2FV-GOfx7YfzcY2NnVkM4uMmr058QUaiAX4wsI3LybFtMB6Xaqy6ijsx2RAUg56nESumo2ecDvs-3PUgDshHJgHluwcEmpDIh7H9w6TEHteDDAs9v4jcu5xX-2_GNyeKUVK8BfGY-qYvgwVwWL5fg-TaiZ6SyrtB_w8dSOpQwtw_at25oeGRpmMh7qkLrbZ6yAkfMIXw",
"size": 91648
}
}]
}
I resolved this using a recursive function, in this way obtains all the text of the message without import the level of depth of the Json answer. If need more explication, please tell me.
private List<string> ObtenerTextoMensaje(IList<MessagePart> partes)
{
var listaTextos = new List<string>();
foreach(var elementoParte in partes)
{
if ((elementoParte.MimeType == "text/plain")|| (elementoParte.MimeType == "text/html"))
{
if (elementoParte.Body.Size != 0)
{
listaTextos.Add(decodificarBase64(elementoParte.Body.Data));
}
}
else
{
if(elementoParte.Parts!=null)
listaTextos = ObtenerTextoMensaje(elementoParte.Parts);
}
}
return listaTextos;
}

Why is attachmentId missing in payload/parts/body from certain emails?

I'm using the gmail API to first generate a list of all attachments, and then download the attachments on demand.
I'm basically calling GET https://www.googleapis.com/gmail/v1/users/me/messages/{ID}?key={YOUR_API_KEY} on all message ids that have an attachment, and then grabbing all the attachmentIds to use with the messages.attachments API later. This works for the majority of my attachments, but I notice for certain emails, the attachment ID is missing.
Heres an example response (with certain parts omitted) from such one of those emails:
{
"id": "114fda8b3fe57cb1",
"threadId": "114fda8b3fe57cb1",
"labelIds": [
"INBOX"
],
"snippet": "",
"historyId": "1027074",
"payload": {
"mimeType": "multipart/mixed",
"filename": "",
"headers": [
],
"body": {
"size": 0
},
"parts": [
{
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "IMG_3746.JPG",
"headers": [
{
"name": "Content-Type",
"value": "image/jpeg; name=\"IMG_3746.JPG\""
},
{
"name": "Content-Transfer-Encoding",
"value": "base64"
},
{
"name": "Content-Disposition",
"value": "attachment; filename=\"IMG_3746.JPG\""
},
{
"name": "X-Attachment-Id",
"value": "f_1ydodxn"
}
],
"body": {
"size": 447292 <------ Here is where I would expect attachmentId to be...
}
},
{
}
]
},
"sizeEstimate": 2873869
}
Here's what the parts/body looks like from a response from a message that has everything I expected:
"body": {
"attachmentId": "ANGjdJ9CRk8VcuxPzOMnGhFQdJpB6vc7xNAZTw5Gav3jZRQkEi3lRvELnjUTEqQtpiJDkKx8IR3qEOyT0wW6LD_tQ3NvPWDucURPaW0xUBJm8gwP-De9hOt7DbciRdfYCX79JWoHtGS2cooTzpAfZwYXc2lmcjCeL_SvgCZAtIEOVs45atTOsx9BCb02aJzNAvga2f3OdsBNbUkCyyZWQZGB0bHQf4q30BmoDFLrBhq67k3f5VOpOxsFl4QDLS5Md-JxhbQ1qsFk_usEUvcOqFsNgckmtj6y0zrKyDDc8g",
"size": 536
}
I can't use GET messages/attachments without the attachmentId... whats going on? What am I missing?
Looks like that was an issue with some older attachments. Should be resolved now.
In my case (using API in Python) the attachmentId was never in the body.
I went here:
https://developers.google.com/gmail/api/reference/rest/v1/users.messages/get
to use "Try this method" window
and I found the attachmentId in parts[1].body

Resources