Guide/Sending from your Google account

Sending from your Google account

Send campaigns and transactional email from Gmail or Google Workspace — the one-click OAuth connection, the app-password shortcut, and how to sign in to Growth with Google.

Growth can send through your ordinary Google mailbox — no domain to verify, no Amazon SES account. There are two ways in, and one of them works right now with no configuration at all.

Which route to take

App passwordConnect Google (OAuth)
Setup2 minutes, inside your Google accountOne-time Google Cloud project
What Growth storesAn app passwordA send-only authorization you can revoke
Read access to your mailNoneNone
RevokingDelete the app passwordOne click in your Google account
WhereCustom SMTP providerConnect Google button

Start with the app password if you want to send today. Move to the OAuth connection when you want a revocable grant and no password in play.

Sending limits — read this first

Google counts recipients, not messages, across To, Cc and Bcc:

  • 500 recipients per day on a personal @gmail.com account
  • 2,000 recipients per day on Google Workspace

Past the cap Google rejects the rest of the day's mail. A 3,000-contact campaign will not go out from Gmail — use Resend, Brevo or Amazon SES for lists that size, and keep Gmail for transactional mail, support replies and small segments.

Route 1 — app password (no configuration)

  1. Turn on 2-step verification at myaccount.google.com/security. Google will not offer app passwords without it.

  2. Create an app password at myaccount.google.com/apppasswords. Copy the 16-character value; Google never shows it again.

  3. In Growth, go to Email marketing → Settings → Sending → New application and choose Custom SMTP.

    FieldValue
    SMTP hostsmtp.gmail.com
    Port465
    Implicit TLSon
    Usernameyour full Google address
    Passwordthe 16-character app password (no spaces)
    From emailthe same Google address
  4. Save, then press Test.

Gmail rejects a From that is not your own address, so keep the From email and the SMTP username identical.

Route 2 — connect your Google account

This is the Connect Google button on the sending settings page. It needs a Google Cloud OAuth client once, then every organization connects in one click.

Step 1 — Google Cloud project

  1. Open console.cloud.google.com and create a project (or reuse one).
  2. APIs & Services → Library → Gmail API → Enable. Without this the connection succeeds and every send fails.

Add exactly three scopes — openid, email, and https://www.googleapis.com/auth/gmail.send.

gmail.send lets an app send as you and grants no ability to read, modify or delete anything in the mailbox. Google classifies it as sensitive, not restricted: publishing needs a brand review, not the paid annual CASA security assessment that full-mailbox scopes require.

Then pick your audience:

  • Google Workspace on your own domain → choose "Internal". No verification, ever. No 100-user cap. No token expiry. Stop here.

  • Personal @gmail.com → choose "External", then press "Publish app".

    Publishing matters more than it looks. While the app sits in Testing, Google expires every refresh token after 7 days — your campaigns would break every week. Published but unverified is fine: users see an "unverified app" warning they can click through, and you are capped at 100 connected accounts. Submit for verification when you open this to customers.

Step 3 — OAuth client

APIs & Services → Credentials → Create credentials → OAuth client ID → Web application. The two flows live on different hosts, which is the single easiest thing to get wrong here:

FlowRedirect URIHost
Gmail connectionhttps://<convex-site>/google/gmail/callbackConvex site (*.convex.site)
Google sign-inhttps://<app-domain>/api/auth/callback/googleyour app domain

The Gmail callback is a Convex httpAction, so it answers on the deployment's .convex.site URL. Sign-in is Better Auth, which this app serves through src/routes/api/auth/$.ts on its own domain and only proxies to Convex — so its redirect URI carries the app origin, never the Convex one.

Add every origin you sign in from. A Web-application client matches redirect URIs exactly, port included, so http://localhost:3000 and http://localhost:3058 are two separate entries — register the ports you actually run pnpm start-all on.

Step 4 — store the keys in Convex

npx convex env set GOOGLE_CLIENT_ID "<client-id>"
npx convex env set GOOGLE_CLIENT_SECRET "<client-secret>"

These serve both Google sign-in and Gmail sending. To isolate them — so a pending Gmail verification cannot affect sign-in — create a second OAuth client and set it separately; Growth falls back to the pair above when these are absent:

npx convex env set GMAIL_OAUTH_CLIENT_ID "<gmail-client-id>"
npx convex env set GMAIL_OAUTH_CLIENT_SECRET "<gmail-client-secret>"

Step 5 — connect

Email marketing → Settings → Sending → Connect Google. Pick the account, accept the consent screen, and you land back on the settings page with the connection created and set as default. Press Test to confirm: unlike an API key, an OAuth grant can be revoked from the Google side, so the test actually mints a token rather than checking the stored value's shape.

The sender address is fixed to the connected account — Gmail accepts no other From. You can still set a display name and a reply-to.

Signing in to Growth with Google

Nothing to build: once GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set in Convex, the Sign in with Google button appears on /auth/signin by itself. It needs the https://<app-domain>/api/auth/callback/google redirect URI from step 3 — the app domain, not the Convex one.

Troubleshooting

"Google did not return a refresh token." The account already authorized Growth, so Google skipped the consent screen. Remove Growth at myaccount.google.com/permissions and connect again.

Sends fail with "The Google account is no longer authorized". The grant was revoked, or the OAuth app is still in Testing and the refresh token aged out after 7 days. Publish the app, then use Reconnect the Google account.

"Request had insufficient authentication scopes." The consent screen is missing gmail.send. Add it, then reconnect — existing tokens do not gain scopes retroactively.

Everything works, then stops mid-campaign. You hit the daily recipient cap. It resets on a rolling 24-hour basis.

Open and click tracking

Gmail sends no delivery webhooks, so Growth tracks opens and clicks itself with its own pixel and link redirection — the same mechanism used for Resend, Brevo and SMTP. Bounces are not reported back, which is the real cost of sending from a mailbox rather than an ESP: watch your list hygiene manually.