If you’re using the Gmail API to send email, specifically using messages.send, you will likely notice some unexpected behavior with respect to HTML and Plain Text message parts. Like all Gmail API methods that deal with an actual email message, emails must be Base64 encoded before being submitted to the API, so my examples below will show the readable RFC-compliant message and the Base64 versions.
If you’re not interested in the details, here are the important things to know regarding sending emails with the Gmail API:
- You can send just a plain text email with the Gmail API, meaning there’s just one “text/plain” MIME part, and that’s it.
- You cannot send just an HTML email with the Gmail API, meaning you cannot send a “text/html” part without a corresponding “text/plain” part. If you try to do this, the API will accept the message, but behind the scenes, it will convert your “text/html” part to a “text/plain” part and add that to the message before it sends the email.
- Setting individual HTML and Plain Text parts when constructing your RFC message is useless because the Gmail API will override the Plain Text part with its own Plain Text part, using the HTML part as its basis. Let’s say you construct your email with an HTML part that looks like “<P>hello there</P>” and a Plain Text part that looks like “hi there.” (Notice that one part says “hello,” and the other says “hi.”) When the Gmail API sends your email, your Plain Text part will be wiped out and replaced with a part that is converted from your HTML part. The actual Plain Text part that gets sent will say, “hello there.”
- Lastly, it’s important to know that the message that appears in your account’s “Sent Mail” folder is not the same message that is sent to the receiver. In example #2 above, if you try to do this, the “Sent Mail” email will show just an HTML part, but the actual received email will have both HTML and Plain Text parts. In example #3 above, if you set your own Plain Text part, the “Sent Mail” email will show your HTML parts and Plain Text parts just as you constructed them for the Gmail API, but again, the actual received email will have a different Plain Text part that was auto-generated by Gmail from your HTML part. This can fool even the most experienced developer, so it’s important that you don’t rely on what’s in your “Sent Mail” folder as proof of the email that was actually sent. This is likely a flaw in the API, where it mistakenly writes the email to the user’s “Sent Mail” folder before sanitizing and sending the email. The better approach would be to sanitize the email, then write it to the user’s “Sent Mail” folder, and simultaneously transmit the email.
- If you choose not to “send” an email, but instead to “insert” the email into someone’s Inbox via the Users.messages.insert API method, then you can set completely different HTML and Text parts and Gmail won’t alter them at all. You can only do this though, if you have OAuth access to the recipient’s Gmail account.
- Behavior with DRAFTs is similar. If you call users.drafts.create and you pass in just a “plain text” part or just an HTML part, then the DRAFT will get created properly with just the part you specified. Meaning, you can have a DRAFT containing just a plain text part, or you can have a DRAFT containing just an HTML part, or you can have a DRAFT containing both HTML and plain text parts. If you then call users.drafts.send on your DRAFT object, that’s where things get funky. If your DRAFT object has just a plain text part or both HTML and plain text parts, the email will be sent and received as expected. If, however, the DRAFT object contains only an HTML part, then your Sent folder will show the email as having sent with just the HTML part, but in actuality, the email will be received with both HTML and plain text parts, with the plain text part being auto-generated by Gmail.
The proof is in the pudding
Now that you know the quirks of sending email with the Gmail API, let me show you some examples as proof. In code, I use Jeffrey Stedfast’s MimeKit framework to assemble and parse messages, but for the examples below, I’ll skip showing off the code that generated each message in favor of showing the end result message and Base64 encoded versions. Additionally, if you’re going to use an online Base64 encoder/decoder to test the API yourself, I recommend this one. It’s the only one that gets the encoding right so that it works with the Gmail API, and it’s the only one that properly decodes the Base64 string created by MimeKit.
In these examples, I’m sending email from my G Suite account, firstname.lastname@example.org, to a test account, email@example.com, that receives mail on a server that I control.
- Sending just a plain text email
The RFC message:The Base64 encoded version:Submission to the Gmail API:
What appears in the “Sent Mail” folder of firstname.lastname@example.org:What is received by the server for email@example.com:Conclusion: it works as expected!
- Sending an email with just an HTML part and NO plain text partThe RFC message:The Base64 encoded version:Submission to the Gmail API:
What appears in “Sent Mail”:What is received by the server:
Conclusion: I submit the email with just an HTML part, but the Gmail API forces a plain text part. Additionally, the “Sent Mail” folder deceptively shows that the email was sent with just an HTML part when it was actually sent with both parts.
- Sending an email with both HTML and plain text parts, but where we set our own plain text partThe RFC Message:Base64 encoded:Submission to the API:
What appears in “Sent Mail”:What is actually received:Conclusion: The Gmail API replaces the plain text part that I’ve set with its own that is converted from the HTML part.