At NORMA, we focus on delivering what we call “Seamless AI” for our clients. These are AI agents seamlessly integrated into the client’s existing tools — like WhatsApp, Gmail, or Google Drive — without requiring additional interfaces. The goal is to enhance user experience by simplifying workflows and reducing the clutter of additional tools, which can often complicate AI adoption.
For example, our Gmail integration uses AI to monitor, process, and respond to emails autonomously, streamlining tasks that typically require manual intervention. By operating behind the scenes, these AI agents deliver value without disrupting established processes, making AI adoption intuitive and frictionless.
This document provides a practical example of how we implement Seamless AI. It explains how to build a Python function that monitors Gmail inboxes, leveraging Google Cloud Pub/Sub and Cloud Functions for real-time automation. Whether you’re new to these technologies or looking for a clear, step-by-step guide to processing newly received Gmail emails and deploying automation on Google Cloud, this document is a great resource to get started.
What seemed like a simple task turned out to require multiple configurations, so I created this guide to make the setup easier to follow (with the help of Google documentation) especially for beginners who might struggle with fragmented documentation. We’ll go through each step, from enabling APIs and configuring OAuth to setting up an autonomous, real-time system to process incoming emails.
Before we start we will go through some various steps:
Step 1: Enable the Required APIs
Step 2.1: Configure the OAuth Consent Screen
Step 2.2: Enable Domain-wide Delegation
Step 3: Create OAuth Credentials
Step 4: Generate token.json
Step 5: Deploy the Code to Google Cloud
1. Create a Pub/Sub Topic
2. Grant Pub/Sub Permissions to Gmail API Service Account
3. Prepare the Cloud Function Code
4. Create a requirements.txt File
5. Deploy the Cloud Function
Step 6: Set Up Push Notifications for Gmail
1. Create a Script to Set Up Notification
2. Run the Script
Step 7: Testing
Let’s get started and turn your Gmail inbox into an active part of your digital workflow!
Step 1: Enable the Required APIs
Why?
Enabling APIs allows your project to use Gmail and Pub/Sub services. These are the backbone of your solution, enabling Gmail to communicate with Pub/Sub and trigger Cloud Functions.
1.Navigate to the Google Cloud Console:
- Go to Google Cloud Console.
2.Enable the Gmail API:
- In the Console, Search for Gmail API and click on it..
- Click Enable to activate the Gmail API for your project.
3.Enable the Pub/Sub API:
- In the same Search Bar, search for Cloud Pub/Sub API.
- Click Enable to activate Pub/Sub for your project.
Step 2.1: Configure the OAuth Consent Screen
Why?
OAuth ensures secure access to your Gmail account. This step sets up an authorization flow to ensure only approved applications can access your data.
- Go to the OAuth Consent Screen:
- In the Google Cloud Console, navigate to APIs & Services > OAuth consent screen.
2. Select User Type:
- Once you’ve chosen the user type, click Create to start configuring the consent screen.
3. Configure App Information:
- App Name: Enter a name for your app (e.g., project_name).
- User Support Email: Provide an email address where users can reach you if they have questions about the app.
- Developer Contact Information: Add an email address that Google can use to reach you about this app.
4. Save and Finalize the Consent Screen:
- Click Save and Continue to save your settings. You may need to fill out additional sections, but for private or internal use, the basic information is usually sufficient.
Step 2.2: Enable Domain-wide Delegation ( Google Workspace)
Why this step?
As we use Google Workspace, this step enables your app to access emails for all users in your organization, not just the creator of the credentials.
You’ll need to enable Domain-wide Delegation. This step requires admin privileges in Google Workspace.
- Enable Domain-wide Delegation for the OAuth Client:
- Go back to APIs & Services > Credentials in Google Cloud Console.
- Find the OAuth 2.0 client ID you created and click to edit it.
- Check the box for Enable Google Workspace Domain-wide Delegation.
- Save your changes.
2. Authorize the OAuth Client in Google Admin Console:
- Log in to the Google Admin Console at admin.google.com using an admin account.
- Go to Security > API Controls > Manage Domain-wide Delegation.
- Click Add New.
- In the Client ID field, enter the OAuth client ID from your credentials.
- In the OAuth Scopes field, enter the Gmail API scope:
https://www.googleapis.com/auth/gmail.readonly - Click Authorize to allow this client to access Gmail data across your organization.
Step 3: Create OAuth Credentials
Why?
OAuth credentials are required to securely authenticate your app with Gmail.
To access the Gmail API, you’ll need to create OAuth 2.0 credentials:Create OAuth Client ID:
- Go to APIs & Services > Credentials in the Google Cloud Console.
- Click Create Credentials > OAuth client ID.
Configure OAuth Client:
- Select Desktop App as the application type.
- Click Create to generate your client ID and client secret.
Download the Credentials:
- Click Download JSON to download the credentials.json file.
- Place this file in your working directory locally with the main.py (we’ll use it to authenticate with the Gmail API).
- Install the Google Cloud SDK to interact with your GCP project from the command line.
- Authenticate using the command:
gcloud auth login - Set your project:
gcloud config set project YOUR_PROJECT_ID
Step 4: Generate token.json
Why?
The token.json file stores the user’s OAuth token, allowing your app to access Gmail without reauthorizing each time.
To access your Gmail inbox, you’ll need an OAuth token. This token will be stored as token.json and allows your application to interact with the Gmail API.
- Create a Script to Generate token.json:
- In your working directory, create a Python script (e.g., generate_token.py) with the following code:
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
# Scope required to read emails from Gmail
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def generate_token():
# Run local server to get authorization from the user
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials to a token file
with open('token.json', 'w') as token_file:
token_file.write(creds.to_json())
print("Token has been saved to token.json")
# Run the token generation process
generate_token()
2. Run the Script:
Run the following command to execute the script and generate token.json:
python generate_token.py
- This will open a browser window asking you to log in and authorize the application. Once you authorize, token.json will be generated in your directory.
Step 5: Deploy the Code to Google Cloud
Why?
Deploying your code to Google Cloud Functions allows it to run serverlessly, triggered by Pub/Sub when new emails arrive.
In this solution, we’ll use Google Cloud Functions to automatically read new Gmail messages when triggered by Pub/Sub.
1. Create a Pub/Sub Topic
- In the Google Cloud Console, go to Pub/Sub > Topics.
- Click Create Topic and give it a name, like gmail-new-email-topic.
- This topic will serve as the middleman between Gmail and your Cloud Function, receiving notifications from Gmail when new emails arrive.
2. Grant Pub/Sub Permissions to Gmail API Service Account
- Go to IAM & Admin > IAM in the Google Cloud Console.
- Click Add to add a new principal (user).
For the Principal, enter:
your_service_account@system.gserviceaccount.com - Assign this principal the Pub/Sub Publisher role specifically for your Pub/Sub topic (gmail-new-email-topic). This will allow Gmail to publish notifications to the topic.
3. Prepare the Cloud Function Code
Once the token.json file is generated, keep it in the same directory as main.py and credentials.json. In main.py, add the following code to handle the new email notifications:
import os
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
import base64
# Define the Gmail API scope
SCOPES = ['https://www.googleapis.com/auth/gmail.modify]
# Helper function to load credentials
def get_credentials():
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
return creds
else:
raise ValueError("No token.json found. Please set up credentials.")
# Function to fetch the latest unread email
def fetch_latest_email(service):
query = f”to:{group_email} is:unread”
results = service.users().messages().list(userId='me', q=query, maxResults=1).execute()
messages = results.get('messages', [])
if messages:
message = service.users().messages().get(userId='me', id=messages[0]['id']).execute()
print("Fetched email:", message)
return message
else:
print("No new unread emails found.")
return None
# Cloud Function that triggers on new Pub/Sub messages
def pubsub_trigger(event, context):
print("Received Pub/Sub message")
try:
creds = get_credentials()
service = build('gmail', 'v1', credentials=creds)
new_email = fetch_latest_email(service)
if new_email:
print("New email fetched:", new_email)
except Exception as e:
print(f"Error handling new email: {e}")
What does this code do?
- Loads OAuth credentials from token.json.
- Connects to the Gmail API to fetch unread emails.
- Logs new email details when triggered by Pub/Sub.
- In the case of filtering with group email :Explicitly query ensures that only the group’s emails are fetched: query = f”to:{group_email}”
4. Create a requirements.txt File
In the same directory as main.py, create a requirements.txt file with the following contents. This file tells Google Cloud which Python libraries to install for your function:
google-auth
google-auth-oauthlib
google-auth-httplib2
google-api-python-client
5. Deploy the Cloud Function
Now that you have all your files (main.py, token.json, credentials.json, and requirements.txt), you’re ready to deploy the function. Run the following command in your terminal:
gcloud functions deploy pubsub_trigger \
--runtime python311 \
--trigger-topic gmail-new-email-topic \
--allow-unauthenticated
This command deploys the pubsub_trigger function, which will automatically trigger every time there’s a new message published to gmail-new-email-topic.
Security Note:
Avoid using — allow-unauthenticated for sensitive projects. Restrict access by configuring IAM permissions for the function.
Step 6: Set Up Push Notifications for Gmail
Why?
Push notifications from Gmail notify Pub/Sub when a new email arrives, enabling real-time monitoring.
Now that your Cloud Function is set up and to enable Pub/Sub to watch the Gmail inbox, we need to configure Gmail to send notifications to your Pub/Sub topic when new emails arrive.
1. Create a Script to Set Up Notifications
Create a new file locally called setup_push_notifications.py with the following code. This script will set up Gmail to send notifications to the Pub/Sub topic we created.
import os
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
TOPIC_NAME = 'projects/YOUR_PROJECT_ID/topics/gmail-new-email-topic'
def setup_push_notifications():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('gmail', 'v1', credentials=creds)
request = {'labelIds': ['INBOX'], 'topicName': TOPIC_NAME}
response = service.users().watch(userId='me', body=request).execute()
print("Push notifications setup response:", response)
except Exception as error:
print(f"An error occurred: {error}")
setup_push_notifications()
2. Run the Script
In your terminal, run the following command to execute the setup script and activate push notifications for Gmail:
setup_push_notifications.py
This command will set up Gmail to notify Pub/Sub every time a new email arrives in your inbox.
Note : This function must be reactivated every 7 days in order to keep the Pub/Sub watching , It’s recommended to deploy it and assigning the Google Scheduler to do the job
Step 7: Testing
- Send a Test Email: Send a test email to your Gmail account to verify that everything is working.
- Check Cloud Function Logs: Go to Google Cloud Console > Cloud Functions > pubsub_trigger function > Logs. Look for log messages confirming that the email details were retrieved.
Security Note
To Improve security:
Access Scope Limitation: With the current setup, if Domain-wide Delegation is enabled, the application could potentially access all emails within the organization. This poses a high security risk and should only be enabled if absolutely necessary and for trusted applications.
Store sensitive files like token.json and credentials.json in a secrets manager, such as Google Secret Manager.
Update your code to fetch these secrets dynamically instead of keeping them in plain text within the project directory. This ensures sensitive information is encrypted and access is tightly controlled.
Regularly rotate your credentials and ensure only authorized entities have access to the secrets.
Conclusion
This documentation focuses on reading emails from your Gmail inbox. However, feel free to modify the code to extend its functionality such as saving email content to a database, forwarding emails, or performing additional processing tasks.
This setup is flexible and can be tailored to fit your specific needs. Whether you’re building a notification system, automating workflows, or integrating Gmail into a larger application, this guide provides a solid foundation to get started.