provider="google"Connect Google: Gmail, Calendar and Drive
Set up a Google Cloud OAuth app so your Python automations can work with each person's own Gmail, Calendar or Drive. Which settings matter, and the gotchas that waste an afternoon.
If a script needs to read someone's Gmail, check their calendar or file something in their Drive, it needs their permission, and Google gives that permission through OAuth. Before anyone can sign in, you need an OAuth app: a record in Google Cloud that says who you are, what you're asking for, and where Google should send people back to.
It takes about ten minutes. This guide walks through it, with the settings that matter for internal automations and the ones you can skip.
What you'll end up with
- A client ID and client secret. Your code never hard-codes these. They go in your project's secrets.
- A consent screen that shows your app's name when people sign in.
- A list of scopes: the permissions your app asks for, like "read, compose and send email".
- One or more redirect URIs: the addresses Google sends people back to after they allow access.
1. Create a project
Go to console.cloud.google.com, open the project picker at the top and create a new project. Name it after the tool or team, such as "Ops automations". If you're on Google Workspace, create it inside your company's organization. That matters in step 3.
2. Turn on the APIs you'll call
Under APIs & Services → Library, search for and enable each API your code uses: Gmail API, Google Calendar API, Google Drive API and so on. Signing in works without this step, but every API call fails with "API has not been used in project".
3. Set up the consent screen
Open Google Auth Platform (it used to be called the OAuth consent screen) and click Get started. Fill in the app name and a support email. Then pick the audience. This is the most important choice in the whole setup:
| Audience | Who can sign in | Review by Google | Good for |
|---|---|---|---|
| Internal | Only people in your Google Workspace organization | None | Company automations. Pick this if you can |
| External, Testing | Up to 100 test users you list by email | None | Personal Gmail accounts, trying things out |
| External, In production, unverified | Up to 100 Google accounts, after an "unverified app" warning | None | A small team on personal Gmail accounts |
Verified apps aren't supported yet. A public app anyone can use without a warning needs Google's verification, and Google requires the redirect URI's domain to be one you own and have verified. The redirect is on callables.app, so a Callables connection can't go through verification today. For automations your own team uses, you won't need it.
Internal needs a Workspace account and a project inside your organization, and it has no user cap, no warnings and no token expiry. For automations your own team uses, it's the right answer almost every time.
The Testing trap: Gmail and Drive scopes are what Google calls restricted. While an External app is in Testing, the sign-ins people make expire after 7 days, and everyone has to connect again every week. If you're on External, add yourself and your teammates under Audience → Test users to get started. Before people rely on the tool, switch it to In production. Without verification you'll see an "unverified app" warning and a 100-user cap, which is fine for a small team.
4. Add the scopes
Under Data access, click Add or remove scopes and add what your code needs. Ask for the narrowest scope that works:
| You want to | Scope |
|---|---|
| Read email | gmail.readonly |
| Read, label, archive and send email | gmail.modify |
| Read calendars | calendar.readonly |
| Work with files your app created or opened | drive.file |
In Callables, the scopes in your code are what gets requested at sign-in, so keep this list and your code in step. Callables also asks for openid and email, so people can see which account they connected.
5. Create the client
Under Clients → Create client, choose Web application and add your redirect URIs:
https://callables.app/connections/callback, for the web apphttp://localhost:8765/callback, if you'll test on your own machine withcall connect(see testing OAuth connections locally)
Click Create and copy the client ID and the client secret right away. Google may not show the secret again, though you can always add a new one.
6. Declare the connection
import requests
from callables import Connection, Group
google = Connection("google", provider="google", scopes=["gmail.readonly"])
gmail = Group("gmail", title="Gmail", connections=[google])
@gmail.deploy(title="Unread count")
def unread_count() -> str:
"""How many unread emails are in your inbox."""
resp = requests.get("https://gmail.googleapis.com/gmail/v1/users/me/labels/INBOX",
headers={"Authorization": f"Bearer {google.token}"})
resp.raise_for_status()
return f"{resp.json()['messagesUnread']} unread"google.token is always the token of the person running the function. Set the client ID and secret as project secrets in Callables, and in .env to test locally:
GOOGLE_CLIENT_ID=1234567890-abc.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...By default the names come from the connection's name: Connection("google", ...) reads GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. To choose your own, pass client_id=Secret("...") and client_secret=Secret("..."). Keep .env in .gitignore.
7. Test, then share
call connect # pick google, sign in, tick every box
call gmail unread_countThen push. Teammates see Connect Google on the form the first time, and every run uses their own account.
When something goes wrong
| What you see | Usually means |
|---|---|
redirect_uri_mismatch | The redirect URI isn't on the client exactly as sent. Check for http vs https, localhost vs 127.0.0.1, and a trailing slash |
access_denied, "app is being tested" | You're on External Testing and this account isn't in the test users |
| "API has not been used in project" | The API isn't enabled (step 2) |
| "Request had insufficient authentication scopes" | The person unticked a box on Google's consent screen. Callables refuses these sign-ins and says which permission is missing |
| People reconnect every week | The app is External and still in Testing |
Next
With the app ready, write a script that runs as each person, and test it on your machine before you push. The full reference is in the connections docs.
Run your scripts on each person's own account
Declare a connection, push, and share. Everyone connects once and every run uses their account.