Gmail and Google Cloud setup
The application sends through the Gmail API as your dedicated Gmail account, using OAuth.
It asks for one Gmail permission, gmail.send (send only; it cannot read the mailbox), plus
the account's email address. Everything below happens in your own Google Cloud project and
your own Gmail account; the developer is never involved after handoff.
Sources: Google's Gmail API sending guide, the Python quickstart, and the OAuth consent documentation (links at the end).
1. Create a Google Cloud project
- Go to https://console.cloud.google.com and create a project (any name, for example "Outreach").
- APIs and Services, Library: search "Gmail API" and click Enable. The Gmail API has no charge.
2. Configure the OAuth consent screen
APIs and Services, OAuth consent screen (Google now calls this "Google Auth Platform", Branding and Audience pages).
- App name: Outreach. Support email: your address.
- User type / audience:
- If the dedicated Gmail account is part of a Google Workspace organisation and the Cloud project belongs to that organisation, choose Internal. Internal apps need no verification and no test-user list. This is the simplest long-term setup.
- If the account is a personal
@gmail.comaccount, choose External. - Scopes: add
https://www.googleapis.com/auth/gmail.send,openidand.../auth/userinfo.email.
External projects: Testing versus In production
An External project starts in Testing status. Testing works for up to 100 listed test users, but Google expires each test user's consent after seven days, so the app would stop sending every week until you reconnect. For an unattended system you want In production:
- On the Audience page, add your dedicated Gmail address as a test user (for the first connection).
- Click Publish app to move to In production.
gmail.sendis a restricted scope. An unverified app in production shows an "unverified app" warning during consent, which you click through once ("Advanced", then continue), and Google caps unverified apps at 100 users in total. With one dedicated account you never approach that cap. Google's verification documentation lists personal use ("you are the only user of your app") among the cases where verification is not required. If Google ever asks for verification, the Internal option above (a Workspace account) avoids the question entirely.
3. Create the OAuth client
APIs and Services, Credentials, Create credentials, OAuth client ID:
- Application type: Web application.
- Authorised redirect URI:
https://<your host>/settings/gmail/callback(for a local test:http://localhost:8000/settings/gmail/callback). - Download the JSON and save it as
data/credentials.jsonon the server (the path inGMAIL_CLIENT_SECRETS).
4. Connect from the admin
Settings, Connect Gmail. Sign in as the dedicated Gmail account, accept the permission.
You land back in Settings with the address filled in. The token is stored at
GMAIL_TOKEN_FILE (default data/gmail-token.json, mode 600) and refreshes itself; the
refresh token stays valid until you disconnect, change the account password with the
"sign out of everything" option, or revoke the app at https://myaccount.google.com/permissions.
Set TRANSPORT=gmail in .env and restart. Until then dryrun builds every message and
records it without sending.
5. What the app sends
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send with a JSON body of one
field, raw: the complete RFC 2822 message (From, To, Subject, Message-ID, text body, the
CV as a multipart/mixed PDF part) encoded base64url. The response's id and threadId
are stored with the history row. Open any sent email in Outreach history to see the exact
request that was built for it.
Sending limits
- Google Workspace accounts: 2,000 messages per day (500 during a trial), applied over a rolling 24 hours; the Gmail API allows up to 500 recipients per message.
- Personal
@gmail.comaccounts: Google's help page states the limit as 500 emails per day; hitting it blocks sending for 1 to 24 hours.
At the intended 1 to 2 emails per day the system never approaches either. Keep the daily cap in Settings low regardless: a dedicated account that suddenly sends hundreds of similar messages is what spam filters look for.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| "OAuth client file not found" | GMAIL_CLIENT_SECRETS points nowhere; copy the downloaded JSON there |
redirect_uri_mismatch on Google's page |
the URI in the client does not match https://<host>/settings/gmail/callback exactly (scheme, host, path) |
| "Gmail token is invalid and cannot be refreshed" | consent expired (Testing status) or revoked; Publish the app and reconnect |
HTTP 403 from the API on send |
the Gmail API is not enabled in the project, or the scope was not granted; reconnect |
HTTP 429 |
daily sending quota reached; the run records the failure and retries on the next schedule |
References
- Sending email with the Gmail API: https://developers.google.com/workspace/gmail/api/guides/sending
- Python quickstart (OAuth flow): https://developers.google.com/workspace/gmail/api/quickstart/python
- App audience, Testing vs In production, verification: https://support.google.com/cloud/answer/15549945
- Restricted scope verification and exemptions: https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification
- Workspace sending limits: https://knowledge.workspace.google.com/admin/gmail/gmail-sending-limits-in-google-workspace
- Personal Gmail sending limits: https://support.google.com/mail/answer/22839