5

Edit: after accepting the answer: my question was about a free Gmail account (I didn't know there was a difference) and the answer is about a paid account (and it is a currect one), the answer showed me that there was a difference and it led me the correct answer to my situation - use a passcode

im trying to send mail using google API and service account, but I'm getting the following erre:

An error occurred: <HttpError 400 when requesting https://gmail.googleapis.com/gmail/v1/users/me/drafts?alt=json returned "Precondition check failed.". Details: "[{'message': 'Precondition check failed.', 'domain': 'global', 'reason': 'failedPrecondition'}]">

this is my code:

from __future__ import print_function

import os.path

from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.service_account import ServiceAccountCredentials

import base64
from email.message import EmailMessage


# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']


def main():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
       creds = ServiceAccountCredentials.from_json_keyfile_name(
        """path_to_cred_file.json""", SCOPES)

    try:
        # Call the Gmail API
        service = build('gmail', 'v1', credentials=creds)
        message = EmailMessage()
        message.set_content('This is automated draft mail')
        message['To'] = '[email protected]'
        message['From'] = '[email protected]'
        message['Subject'] = 'Automated draft'

        # encoded message
        encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

        create_message = {
            'message': {
                'raw': encoded_message
            }
        }
        # pylint: disable=E1101
        draft = service.users().drafts().create(userId="me",
                                                body=create_message).execute()
    except HttpError as error:
        # TODO(developer) - Handle errors from gmail API.
        print(f'An error occurred: {error}')


if __name__ == '__main__':
    main()
2
  • Hi instead of editing in an answer in your question, it's better if you post it as an answer. Commented Nov 15, 2022 at 10:06
  • @PetterFriberg usually you are right, but if il post it as an answer ppl will not look at it because it has an accepted answer Commented Nov 15, 2022 at 10:12

1 Answer 1

7

"Precondition check failed" usually means that you're trying to do something that cannot be done. In this case you're trying to send an email from a service account, which is not possible. This answer has a link to a relevant thread from the Google documentation. They say the following:

Service accounts dont work with gmail unless you set up domain wide delegation to a Gsuite account. The reason being is that a service account is its own user you need to delegate its permission to access your gmail account. This will only work with a gsuite domain email address.

This means that the service account by itself cannot send messages, but instead needs to be delegated access to a regular user account in order to send emails. To do this you can add the following line after your creds:

    delegated_creds=credentials.with_subject("[email protected]")

    #where someuser@ is the email of the user that you're sending email as

After that you can use delegated_creds instead of creds to call the service.

Also, you seem to have gotten your sample from Google's guide, but note that your sample creates a draft instead of sending an email. The API call to send emails is a little different. With that in mind here's a complete example based on your code which worked for me:

#all the imports

SCOPES = ['https://mail.google.com/']

def main():

    creds = None
       creds = ServiceAccountCredentials.from_json_keyfile_name(
        """path_to_cred_file.json""", SCOPES)
     
       delegated_creds=credentials.with_subject("[email protected]")

    try:
        # Call the Gmail API
        service = build('gmail', 'v1', credentials=delegated_creds) 
        message = EmailMessage()
        message.set_content('This is automated draft mail')
        message['To'] = '[email protected]'
        message['From'] = '[email protected]'
        message['Subject'] = 'Automated draft'

        encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

        create_message = {
            'raw': encoded_message
        }

        email = service.users().messages().send(userId="me", 
                                                body=create_message).execute()
        except HttpError as error:
            # TODO(developer) - Handle errors from gmail API.
            print(f'An error occurred: {error}')


if __name__ == '__main__':
    main()

Finally, as explained in the thread I linked, this only works for Google Workspace accounts and you cannot delegate access to free Gmail accounts, so keep that in mind.

Sign up to request clarification or add additional context in comments.

1 Comment

thank you very much for the answer, with your answer I understood that I can't use a service account to send mail with my free Gmail account. and I was able to solve my problem :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.