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'] = 'somemail@gmail.com'
message['From'] = 'somemail@gmail.com'
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()
"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("someuser@yourdomain.com")
#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("someuser@yourdomain.com")
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=delegated_creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'somemail@gmail.com'
message['From'] = 'somemail@gmail.com'
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.