Sending Email with the Gmail API in .NET / C#

I’ve a .NET web application with several Google API integrations and wanted to send Gmail as well. It took me all day, and I found precious little (useful) documentation, so it seemed worth a blog post.

At the start of the day I already had OAuth2 talking with Google. I did have to tweak my setup a bit, but authentication was already a solved problem with my integration.

I had to pull in two NuGet packages to get it to work:

Install-Package Google.Apis.Gmail.v1
Install-Package AE.Net.Mail

Update: In the comments below, Jon notes that using MimeKit instead of AE.Net.Mail makes it easier to send HTML-formatted email.

And here’s the code that sent an email:

using System.IO;
using System.Net.Mail;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;

public class TestEmail {

  public void SendIt() {
    var msg = new AE.Net.Mail.MailMessage {
      Subject = "Your Subject",
      Body = "Hello, World, from Gmail API!",
      From = new MailAddress("[you]@gmail.com")
    };
    msg.To.Add(new MailAddress("yourbuddy@gmail.com"));
    msg.ReplyTo.Add(msg.From); // Bounces without this!!
    var msgStr = new StringWriter();
    msg.Save(msgStr);

    // Context is a separate bit of code that provides OAuth context;
    // your construction of GmailService will be different from mine.
    var gmail = new GmailService(Context.GoogleOAuthInitializer);
    var result = gmail.Users.Messages.Send(new Message {
      Raw = Base64UrlEncode(msgStr.ToString())
    }, "me").Execute();
    Console.WriteLine("Message ID {0} sent.", result.Id);
  }

  private static string Base64UrlEncode(string input) {
    var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
    // Special "url-safe" base64 encode.
    return Convert.ToBase64String(inputBytes)
      .Replace('+', '-')
      .Replace('/', '_')
      .Replace("=", "");
  }
}

Why?

The .NET code samples in the Google documentation don’t tell you how to instantiate the Message class. That’s the hard part. There is a deep and complicated structure to it, and I couldn’t get it to work. The Google Service complained, basically saying, “I want you to use the Raw property.” And the Raw property is, well, raw: “The entire email message in an RFC 2822 formatted and URL-safe base64 encoded string.” Couple tricks here:

  • RFC 2822 is complicated.
  • System.Net.Mail.MailMessage doesn’t give access to the raw data (even though it must implement RFC 2822 internally).
  • URL-safe base64 encoding isn’t exactly what Convert.ToBase64String does.

Thankfully AE.Net.Mail.MailMessage provides the Save() method to get the raw RFC 2822 message, and just a little tweaking is necessary to get a URL-Safe base 64 encoding.

Hope saves someone some time! I did a lot of Googling today, and an article like this one would have saved me a ton of time.

This entry was posted in Programming, REST. Bookmark the permalink.
  • Torbin Pace

    Just wanted to thank you for the informative explanation. I spent several hours searching google for any examples of how to use the v1 API to send messages and yours has been the only one I found, luckily for me it was exactly what I needed.

    • pettys

      Really glad it helped, Torbin!

  • Jon Glick

    How do you send an HTML email with attachments? Using your code, I can send plain text with attachments, but as soon as I add ContentType = “text/html” for the Body, my attachments come through as encoded text rather than attachments. It’s like it’s ignoring the content type setting on the attachments.

    • Jon Glick

      I was able to accomplish this by using MimeKit instead of AE.Net.Mail. I constructed my message using a System.Net.Mail.MailMessage, then:

      MimeMessage message = MimeMessage.CreateFromMailMessage(mailMessage);

      var result = gmail.Users.Messages.Send(new Message {
      Raw = Base64UrlEncode(message.ToString())
      }, “me”).Execute();

      Thanks so much for your post! Wish I had found it yesterday. Would have saved me many hours of fruitless searching. 🙂

      • pettys

        Thanks, Jon. I’ve added a note to the article to include what you’ve learned. I may end up switching to that as well if my client needs HTML emails or attachments!

        I definitely know what you mean about the hours of fruitless searching!

      • Allen King

        Doesn’t work for me. In GMail I see the original message (looks perfect) but also see Bounce for the message. “An error occurred. Your message was not sent.” It appears there is an issue with Base64 encoding using Base64UrlEncode.

        • Jon Glick

          I use this exactly as written and I’ve never had an issue with it. Are you sure the issue is occurring in that method?

  • ravinder

    earlier i was using simple gmail smtp settings to send mail but now i am tring to implement gmail api. i tried your code but may i know what is Context.GoogleOAuthInitializer here?

    • pettys

      Hi ravinder – that is a piece of code you’ll have to provide that tells the GmailService where to gets the OAuth authentication information. In my app this was a separate piece that I wrote, based on the way my app was using OAuth.

      The scope of this particular article focuses on how to properly call gmail.Users.Messages.Send(), because it’s strangely hard and very undocumented. The OAuth authentication part is not covered here. If you haven’t quite made it that far in your own integration, I can see how that part would be confusing!

      In order to use GmailService, generally you need your app to authenticate with Google via OAuth. If you need help figuring out the authentication parts, I’ll have to refer you back to the official documentation on that. But the good news is that part of the integration is very well documented there, and is basically what one should expect when integrating with an OAuth service.

      Hope this clarifies!
      Jason

      • ravinder

        hello jason. thanks your code worked fine after auth initialization. but i am only able to send me to my self . when i try to send mail to other accounts the mail gets bouce to me back and i get errror 403 forbidden
        and not getting solution . i tried this from rest client as well but same issue there also.
        can you please help me to do this..

        • pettys

          I raviner – glad this code is working for you! I’m guessing your current issue is you’re trying to send an email from an address that your current authentication identity isn’t allowed to send from, and so gmail is denying to send it, as expected.

          I’d be glad to help you explore solutions to the broader problem you are trying to solve, but of course also need to pay my bills. I’m afraid I can’t donate the kind of time you’re asking for. If you’d like additional assistance from me please contact me through my consulting firm at http://pettys.consulting so we can make arrangements so I can help you.

          Of course you might be able to find people willing to help you further at no charge at http://stackoverflow.com, and I’m guessing the official Google documentation problem contains the raw information you need.

          Best of luck!

  • grmbl

    Thanks for your contribution! Google only mentioned java/python in their examples and I could not get base64string from a non-serializable MailMessage object… So kudos to you! 😉

    • pettys

      grmbl, I know what you mean! The .NET Google doc in this area was basically:

      To send an email:
      gmail.Users.Messages.Send(/* put magic here */).Execute();

      Really glad this saved you some time.

  • Sam W

    do you know how to reply to emails using RFC 2822?

  • Send().Execute() never returns. This may have to do with one of a couple of things.
    1. I am testing this against an account that has two-factor authentication enabled.
    2. I am attempting to use a service account since this is essentiall a web service that is sending out notifications.

    Have you encountered anything like that?

    • Hi Cory – I have not encountered Send().Execute() never returning, but I’ve also not tested against an account with 2-factor auth enabled. I agree with you that is a likely source of the problem.

      For troubleshooting-purposes, could you try with a non-two-factor auth account, or temporarily disable two-factor auth, just for the sake of being sure that’s the source of the problem?

      I did have issues with other Google API methods not returning when I was using the NON-async versions of the API inside of a heavy async-driven situation (an Akka.Net actor). When I switched to using async versions of the Google API the problem went away.

      In that situation I had used Google.ApplicationContext.RegisterLogger() to view the debug logging from the Google code — maybe that would help you, too.

      I’d be interested in what you find!

      Jason

  • Bobby Soliz

    A little over a year later, still valuable…thanks!

  • Toni Rovira Costa

    Great post!!!
    One question: my code returns “Insufficient Permission [403]”.
    I added all scopes in scopes array… But the error continue. Any idea?? Thanks!!!!

    • Hi Toni – I guess that would be a basic authentication/authorization issue, not related to the formatting concerns solved. I would suggest Googling for “gmail api insufficient permission” and see what suggestions you find. Good luck!

      • Toni Rovira Costa

        Ok Jason, thanks!!!
        Toni

  • MartinRomania

    Thanks Jason.
    Even a year later your post is still saving time. At least this one saved mine.

    • Thanks for the note, MartinRomania – I’m very gratified to hear that this helped you.

  • Benjamin Thomas Parnau

    Context does not contain a definition for GoogleOAuthInitializer.
    And/or Context does not have a namespace.
    Any ideas?

    • Benjamin, see the comment below by ravinder, and my reply. Does that explain?

    • In addition to referring to you the info I gave to ravinder below, I just updated the blog post, adding a comment to clarify what Context is all about. Thanks for pointing out that it was unclear!

  • Juan

    hi, i am getting this error.

    Login Required [401]

    Errors [

    Message[Login Required] Location[Authorization – header] Reason[required] Domain[global]

    ]

    I think is because I constructed the gmailservice like this:

    var gmail = new GmailService();

    I get that I must give the constructor some kind of reference to the OAuth2, but I havent found a way to do it.

    I am using the default MVC WebApp in visual studio 2013, on which you just have to uncomment a piece of code in Startup.Auth.cs to enable gmail validation.

    Could you help me please.

  • Nagababu

    Hi Jason. i am facing some problem with send mails to gmail by using google api..please help me

  • kiquenet kiquenet

    What is (Context.GoogleOAuthInitializer ? full source code ?

    • kiquenet, as the code notes, the way you construct GmailService will be different than the way I did. Further explanation is given in my response to ravinder in this discus comment thread.