Search code examples
pythongmail-apiservice-accounts

Send e-mail via Gmail API from python script with ServiceAccount


I would like to create a python script that will be able to send e-mail after getting triggered by something. (I have a Google Workspace)

  1. So I created a ServiceAccount and a Key for it. It is JSON format.
  2. On the Google Workspace / API-Controller I've created a a rule for the ServiceAccount(with UniqueID) with https://www.googleapis.com/auth/gmail.send permission.

Here is my python script:

import base64
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

scope_gmail_api = ['https://www.googleapis.com/auth/gmail.send']
authentication_credential = "credential.json"

def create_email(subject, message_text, to, from_email):
    message = f"From: {from_email}\nSubject: {subject}\nTo: {to}\n\n{message_text}"
    return {'raw': base64.urlsafe_b64encode(message.encode("utf-8")).decode("utf-8")}

def create_email_service():
    creds = None
    creds = ServiceAccountCredentials.from_json_keyfile_name(
    authentication_credential, scope_gmail_api)

    service = build('gmail', 'v1', credentials=creds)
    return service

gmail_service = create_email_service()

def main():

    subject = "Test Subject"
    message_text = "Test"
    to_email = "destination@domain.com"
    from_email = "source@domain.com"
    message = create_email(subject, message_text, to_email, from_email)

    try:
        sent_message = gmail_service.users().messages().send(userId='me', body=message).execute()
        print(f'E-mail sent: Message ID: {sent_message["id"]}')
    except Exception as e:
        print(f'Error: {str(e)}')

if __name__ == '__main__':
    main()

Here is the error message:

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

I've read THIS StackOverflow comment but with the delegated_credentials = creds.with_subject('admin@mydomain.com') the error message is:

AttributeError: 'ServiceAccountCredentials' object has no attribute 'with_subject'

What did I miss?

UPDATE: I also tried with:

credentials = service_account.Credentials.from_service_account_file(
        authentication_credential, scopes=scope_gmail_api)

delegated_credentials = credentials.with_subject('admin@mydomain.com')

ERROR still:

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


Solution

  • you could try this

    import base64
    from googleapiclient.discovery import build
    from google.auth.transport.requests import Request
    from google.oauth2 import service_account
    
    scope_gmail_api = ['https://www.googleapis.com/auth/gmail.send']
    authentication_credential = "credential.json"
    
    def create_email(subject, message_text, to, from_email):
        message = f"From: {from_email}\nSubject: {subject}\nTo: {to}\n\n{message_text}"
        return {'raw': base64.urlsafe_b64encode(message.encode("utf-8")).decode("utf-8")}
    
    def create_email_service():
        credentials = service_account.Credentials.from_service_account_file(
            authentication_credential, scopes=scope_gmail_api)
        delegated_credentials = credentials.with_subject('admin@yourdomain.com')
    
        # Check if the token is expired and refresh if necessary
        if delegated_credentials.valid is None or not delegated_credentials.valid:
            delegated_credentials.refresh(Request())
    
        service = build('gmail', 'v1', credentials=delegated_credentials)
        return service
    
    gmail_service = create_email_service()
    
    def main():
        subject = "Test Subject"
        message_text = "Test"
        to_email = "destination@domain.com"
        from_email = "source@domain.com"
        message = create_email(subject, message_text, to_email, from_email)
    
        try:
            sent_message = gmail_service.users().messages().send(userId='me', body=message).execute()
            print(f'E-mail sent: Message ID: {sent_message["id"]}')
        except Exception as e:
            print(f'Error: {str(e)}')
    
    if __name__ == '__main__':
        main()