Spurlock Studios
Contact
Share LinkedIn X
A scuffed work smartphone with a blank glowing circular button. Thesis: AGENCIES ONBOARD MULTIPLE CLIENTS GOOGLE.

Agencies onboard multiple clients for Google OAuth on a self-hosted n8n instance by standing up a Google Cloud project, configuring the OAuth consent screen, pasting the exact redirect URL n8n shows (/rest/oauth2-credential/callback on your public origin), then creating one n8n Google credential per client and walking that person through consent — including Google’s unverified-app warning when the app is still in Testing or still requesting unverified sensitive scopes. Self-hosted n8n does not get n8n Cloud’s Managed OAuth2. Custom OAuth2 or it does not connect.

This is the Google Cloud OAuth client and the consent click. Folders, Projects, instance-per-client, and the license question of hosting client tokens on your box live on multi-client n8n isolation. Do not flatten those pages. The production spine — pause on auth drift, do not retry-storm a 401 — is the Production n8n handbook. If a Gmail send keeps dying on expired consent, park the payload on a dead-letter queue instead of reconnecting in a loop.

I have shipped 500+ automations. The agency graphs that survive onboarding are boring: a named GCP project, a redirect URI that matches the credential modal character-for-character, and a client who already saw a screenshot of “Google hasn’t verified this app” before they sat down.

A shared “agency Google” is not an onboarding shortcut. It is a mixed mailbox with extra steps.

The short answer

  • Custom OAuth2, every time. n8n Cloud can Sign in with Google with no Cloud Console work on listed nodes. Self-hosted cannot. You build the app in Google Cloud.
  • One n8n Google credential per client. One Cloud OAuth client ID per client is the honest default. Sharing one Client ID across ten mailboxes is how a revoke takes the floor with it.
  • Redirect URL is copy-paste, not invented. n8n prints OAuth Redirect URL on the credential. That string goes into Google’s Authorized redirect URIs. Behind a proxy, set N8N_WEBHOOK_URL so the printed string is not http://localhost:5678/....
  • Consent screen is a product. Internal for one Workspace. External defaults to Testing: test users, seven-day grants, a warning. Brief it.
  • Unverified is expected until it is not. Google hasn’t verified this app is the tester path, not a bug in n8n. Add the consenting account as a test user. Do not skip the briefing.
PieceOwnerFailure if you skip it
GCP projectClient, unless they have no Cloud org and you documented whyAgency-branded consent on a client mailbox
OAuth consent screenSame projectAccess denied, or a warning nobody explained
OAuth client (Web application)Same projectinvalid_client
Redirect URICopied from n8nredirect_uri_mismatch
n8n credentialOne per client, namedShared Gmail, mixed tokens
Consent clickThe client’s Google userYour intern authorized their personal Drive

Green Sign in with Google on your Gmail is homework. A client admin who consented their Workspace user into their named credential is onboarding.

What does agency Google OAuth onboarding actually mean?

It means you take a client from “we use Gmail and Sheets” to a named n8n credential that can call Google APIs as that client’s user or Workspace, with tokens stored in that n8n credential record. It is not “paste one Client ID into every Gmail node.” It is not “the intern logs in once and we reuse it.”

n8n’s own Google docs split the work into five steps: Cloud project, enable APIs, consent screen, OAuth client, then finish the n8n credential. That sequence is identical for single-service (Gmail node, Sheets node) and generic Google OAuth2. Generic adds scopes you type by hand. Single-service does not.

JobHonest meaningCounterfeit
OnboardClient Google user consents into a credential named for themAgency founder consents “for now”
Isolate Google appsSeparate Cloud project / OAuth client per clientOne Client ID, many Sign-ins
Isolate n8n tokensSeparate n8n credential recordsOne credential, swap which mailbox later
Isolate tenancySeparate instance or licensed Projects — other postFolders named acme/
DoneClient can revoke without killing sibling clients“We’ll rotate later”

Decision list — if you cannot check these, you have not onboarded:

  1. Can you point at the GCP project id in writing?
  2. Does the n8n credential name include the client, not Google account?
  3. Did a person at the client complete Sign in with Google, not you?
  4. Is the redirect URI in Google identical to the credential modal, including https and the path?
  5. Did that person see (or skip, because Internal) the unverified warning on purpose?

Onboarding is a consent event. A JSON credential export you copied from staging is not a consent event.

Why is Custom OAuth2 mandatory on self-hosted n8n?

Because Managed OAuth2 is an n8n Cloud product. The Google credentials hub lists Calendar, Chat, Contacts, Docs, Drive, Gmail, Sheets, Slides, Tasks, and YouTube as Managed on Cloud. Self-hosted is told to use Custom OAuth2: you create the app in Google Cloud Console and paste Client ID and Client Secret into n8n.

That is the whole hosting split for Google. Do not open a Cloud Console “because we are serious” on n8n Cloud if Managed covers the node. Do not look for a Sign in with Google shortcut on self-hosted that skips Console. It is not there.

HostGoogle OAuth you actually getConsole work
n8n Cloud, listed nodes, Managedn8n’s app; Sign in with GoogleNone required
n8n Cloud, you still want your own appCustom OAuth2Full five steps
Self-hosted, any Google nodeCustom OAuth2 onlyFull five steps
Self-hosted, generic OAuth2 APICustom + scopes you typeSame, plus scope list

Procedure if someone asks “can we just use the Cloud button on our VPS?”:

  1. Open the credential. If the only path is Client ID / Client Secret / Sign in with Google, you are on Custom.
  2. Confirm WEBHOOK_URL / N8N_WEBHOOK_URL is the public origin, not an unset default.
  3. Create or open the GCP project.
  4. Stop looking for Managed. It will not appear.

Service accounts are a different credential type. n8n supports them on some Google nodes (Sheets, Drive, BigQuery, Docs). n8n is explicit that Gmail via service account wants domain-wide delegation, which Google discourages, and behavior can be inconsistent. n8n recommends OAuth2 for Gmail. Agency onboarding for mail is OAuth, not a JSON key you dropped in a shared Drive.

Custom OAuth2 is the self-hosted door. Managed is a Cloud convenience. Mixing those sentences in a sales deck is how a client thinks Google will “just work” on your Docker box.

Why one Google credential per client, not one agency login?

Because the n8n credential is where the refresh token lives. One Sign in with Google mints a grant for one Google user into one credential. Reusing that credential on Client B’s workflows means Client B’s Sheets node is Client A’s Google user. That is not multi-client. That is a confused deputy.

Worse pattern: one Google Cloud OAuth client ID, many n8n credentials. Each Sign-in still hits the same app. Consent branding is yours. The unverified-app user cap, if it applies, is on that project. A Workspace admin who blocks “unverified apps” blocks every client at once. A revoke of the Cloud client secret kills every n8n credential that stored it.

PatternWhat Google seesWhat n8n storesHonest for agencies?
One agency Gmail, one n8n credential, every client workflowOne userOne grantNever for production mail or Drive
One Cloud OAuth client, many n8n credentialsOne app, many usersMany grants, one Client ID/secretBetter than shared Gmail; still one blast radius on secret/cap/branding
Per-client Cloud OAuth client, per-client n8n credentialSeparate appsIsolated grants and secretsThe default
Per-client Cloud project plus the row aboveSeparate consent screens, test lists, verificationIsolated grantsBest when clients will own the project

Checklist before you call a Google node “Client A’s”:

  • Credential name is acme-gmail-oauth (or similar), not Google
  • Client ID in n8n matches the OAuth client in Acme’s Cloud project (or a project you labeled Acme)
  • The Google user who consented is an Acme mailbox, not you@agency
  • Client B’s workflows cannot select Acme’s credential unless an admin intended sharing
  • Offboard = delete that n8n credential and revoke the Cloud client or the user’s grant — not “we’ll rename the credential”

n8n can share a credential so other users use it without seeing the secret, on plans that include sharing. That is a collaboration feature. It does not make one Gmail grant safe for two companies.

If your instance is already a mixed kitchen, stop onboarding new Google clients onto the shared Client ID. New clients get a new Cloud client. Old ones get a migration window. Do not “fix” ten years of shared Gmail in the same afternoon you add Client Eleven.

Who should own the Google Cloud project?

The client, whenever they have a Google Cloud org or a Workspace that can own one. Then the consent screen can be Internal (org users only, no unverified screen, no 100-user cap on that Internal path). You still help click the buttons. You do not become the app.

Agency-owned projects are for clients who will not open Cloud Console and have signed something that says you operate an OAuth app on their behalf. Even then: one project per client, not a studio mega-project named n8n-prod.

Google’s own when verification is not needed page is the decision table. Internal apps used only by people in the same Workspace / Cloud Identity org, with the project owned by that org, skip the unverified screen and the user cap. Personal-use and Testing apps can keep going with click-throughs and caps. A public product that wants many users and sensitive scopes verifies.

Client situationProject ownerUser typeWhy
Workspace, only org users will consentClient orgInternalGoogle’s Internal path is built for this
Mixed consumer Gmail + WorkspaceClient projectExternalInternal cannot consent @gmail.com
Agency sandbox, no client Cloud access yetAgency, labeled with client nameExternal + TestingTemporary; calendar-dead grants in seven days
You want one studio app “for all clients”Do not—Shared branding, shared cap, shared secret

Procedure to pick ownership in the kickoff:

  1. Does the client have Workspace? If yes, prefer their org owns the project.
  2. Will any consenting user sit outside that org? If yes, Internal is a lie — use External.
  3. Can a client admin create a Cloud project this week? If no, you create one named for them, then transfer when they can.
  4. Write the project id in the SOW. “We’ll use ours” without an id is how you lose the project six months later.

Do not invent a Google “projects per org” number here. Google’s quotas move. If a create fails, read the error in Cloud Console. Do not quote a forum cap.

Transfer of a project is a Cloud IAM event. If you skip it, you still own the OAuth client when the retainer ends. That is an offboarding bug, not a feature.

You configure it before you mint the OAuth client, in the same GCP project, following n8n’s consent-screen steps. Google now routes this through Google Auth Platform. App name and support email are what the human reads. Audience is Internal vs External. Authorized domains must include the domain of your n8n instance (self-hosted) — n8n Cloud users add n8n.cloud instead.

External defaults to Testing. n8n’s docs are blunt: only Google accounts you add as test users can complete the flow. Everyone else gets access denied. That is not n8n being picky. That is Google.

Consent fieldWhat to putAgency trap
App nameThe client’s product or “Acme n8n”, not your studio nicknameClient sees “Spurlock sandbox” on their Gmail
User support emailAn inbox a human readsA no-reply you do not watch
Audience / user typeInternal if truly org-only; else ExternalInternal, then a contractor on @gmail.com
Authorized domainsHostname of self-hosted n8nlocalhost as a “domain”; leftover ngrok-free.app
Test users (External + Testing)Every Google account that will click Sign inYou tested with yours; the client is not on the list
Scopes on the screenThe scopes the n8n node will requestConsole lists three; n8n requests a restricted Gmail scope

Numbered setup (n8n’s sequence, agency notes in the last column of your runbook):

  1. Google Cloud Console → correct project selected in the top dropdown.
  2. APIs & Services → OAuth consent screen → Get started.
  3. App name, support email, Next.
  4. Audience: Internal or External. Next.
  5. Contact email Google can use about the project. Accept the User Data Policy. Create.
  6. Branding → Authorized domains → add the n8n host domain → Save.
  7. If External: Audience → Test users → add the client’s consenting account before they sit down.

Scopes you request in the n8n credential must match what you declared. Google’s unverified apps help says a mismatch between requested scopes and the consent-screen configuration is enough to show the unverified screen. Least privilege is not a slogan. Extra Gmail scopes are how a Sheets-only job inherits a restricted-scope warning.

If you ever submit verification, Google’s unverified apps page wants a privacy policy URL, site ownership in Search Console, and justification for sensitive or restricted scopes. That is a product launch, not an onboarding meeting. Most agency retainers never need it if User type is Internal. Do not start a verification ticket to “look professional” on a three-user Workspace.

Publish-to-production superstition: publishing without verification, while requesting sensitive or restricted scopes, still shows an unverified warning. Google’s audience page says that out loud. Testing vs In production is not “the warning goes away.” Verification is.

Consent-screen checklist before you create the OAuth client:

  • Correct project in the Cloud top dropdown
  • App name a client would recognize on their phone
  • User type matches who will actually click
  • Authorized domain is the n8n host, not a leftover tunnel
  • Test users added if External + Testing
  • Declared scopes will match the n8n credential
  • No verification ticket opened “just in case”

What redirect URL does self-hosted n8n send?

n8n shows an OAuth Redirect URL on the Google credential. You copy that exact string into the Cloud OAuth client’s Authorized redirect URIs. n8n documents the local shape as http://localhost:5678/rest/oauth2-credential/callback. Google allows localhost for development. A public production box must present a public URI, usually https://<your-n8n-host>/rest/oauth2-credential/callback.

n8n composes public URLs from N8N_PROTOCOL, N8N_HOST, and N8N_PORT. Behind a reverse proxy that is wrong, because n8n still thinks it is on 5678. Set N8N_WEBHOOK_URL to the HTTPS origin (https://n8n.example.com/) and N8N_PROXY_HOPS=1. As of n8n 2.35.0, WEBHOOK_URL is a deprecated alias of N8N_WEBHOOK_URL and logs a warning. N8N_EDITOR_BASE_URL is the public editor URL (emails, SAML). Do not assume it is the OAuth callback. The credential modal is the source of truth.

If you changed env after the credential already existed, delete and recreate the Google credential. Operators routinely report the callback baked at create-time. Pasting a new URI in Google while n8n still sends the old one is redirect_uri_mismatch.

What you see in n8nWhat it meansFix
http://localhost:5678/rest/oauth2-credential/callback on a VPSPublic URL not configuredSet N8N_WEBHOOK_URL, restart, recreate credential
https://n8n.example.com/rest/oauth2-credential/callbackHealthy default REST pathPaste exactly into Google
Path is not /rest/...N8N_ENDPOINT_REST is not restRegister the path n8n actually prints
Google error redirect_uri_mismatchString mismatch: scheme, host, port, slash, pathCopy from n8n again; do not retype
Callback 401 after Google already approvedSession / auth-on-callback mismatchSee n8n 2.0 N8N_SKIP_AUTH_ON_OAUTH_CALLBACK; confirm for your version

n8n 2.0 breaking changes flipped N8N_SKIP_AUTH_ON_OAUTH_CALLBACK from default true to default false (callback requires an authenticated n8n session). Google’s redirect lands in the browser that started Sign in. If that browser still has the n8n session, you are fine. If the callback host differs from the editor host, cookies die and you get a generic Unauthorized. Confirm against the changelog for the version you run. Do not cargo-cult =true onto a box that already completes consent.

Redirect URI checklist:

  • Open a new Google credential so you see the current URL
  • Copy, do not retype
  • Google OAuth client → Web application → Authorized redirect URIs → paste
  • Scheme matches (http only for localhost; production is https)
  • No trailing path drift (/callback vs /callback/)
  • Test the URI in a browser; n8n should complain about missing OAuth params, not Nginx 404
  • Recreate the n8n credential after any N8N_WEBHOOK_URL change

Google’s web-server OAuth docs treat localhost as a special development case. A public hostname is expected to be HTTPS. If Console refuses http://n8n.example.com/..., that is Google, not n8n. Put TLS on the box, set N8N_WEBHOOK_URL=https://.../, recreate the credential, paste the new URI.

Smoke-test the callback before the client meeting:

  1. Copy the URI from n8n.
  2. Open it in a logged-out browser window.
  3. You want an n8n OAuth error about missing parameters, not a proxy 404, not a Cloudflare login, not the editor homepage.
  4. Only then paste it into Google.

A localhost redirect on a production hostname is the first ticket I see when an agency “finished OAuth” on a laptop and then pointed DNS at Docker.

How do you enable APIs and create the OAuth client?

Enable only the APIs the nodes need, in the same project as the consent screen. n8n’s enable-API list is the map: Gmail API for the Gmail node; Sheets API and Drive API for Sheets; Docs and Slides also need Drive. Ads needs a developer token. Vertex needs Vertex plus Cloud Resource Manager. Do not enable the kitchen sink “so we don’t come back.” Extra APIs are how someone later adds a Gmail node to a Sheets-only app and surprises the consent screen.

Then create OAuth client ID, type Web application, name it so you can find it at 2am (acme-n8n-web). Paste n8n’s redirect URI. Create. Copy Client ID and Client Secret once into the n8n credential. The secret is not Slack-safe.

Node you will attachAPIs to enable (n8n docs)Credential flavor
Gmail / Gmail TriggerGmail APIOAuth2 single-service
Google Sheets / TriggerSheets API and Drive APIOAuth2 single-service
Google Docs / SlidesDocs or Slides and Drive APIOAuth2 single-service
Drive / Drive TriggerDrive APIOAuth2 single-service
CalendarCalendar APIOAuth2 single-service
HTTP Request to a Google APIThe API you callGeneric Google OAuth2 + explicit scopes
BigQuery / some Drive jobsMatching APIOAuth2 or Service Account where n8n allows

Numbered create path:

  1. Console → correct project.
  2. APIs & Services → Library → search → Enable.
  3. Credentials → Create credentials → OAuth client ID.
  4. Application type: Web application.
  5. Name it for the client.
  6. Authorized redirect URIs ← n8n OAuth Redirect URL.
  7. Create. Copy ID and secret into n8n. Store the secret in the client’s vault, not in Notion.

invalid_client almost always means the ID or secret in n8n does not match Console — whitespace on paste, old secret after a rotate, or the wrong project’s client. n8n’s troubleshooting says copy both values fresh.

Google Ads, Perspective, and a few others have extra access gates. If the Library page says request access, that is a Google process. n8n cannot skip it.

Do not mint a Desktop or iOS client “because the dropdown was there.” n8n’s Google docs say Web application. Wrong client type is a redirect and secret mismatch you will not debug by restarting Docker.

How do you create and connect the n8n credential?

Create the credential in n8n first far enough to see the redirect URL, configure Google, then finish n8n: paste Client ID and secret, set scopes if generic, then Sign in with Google. The client’s Google user must complete that click. Then Save.

Single-service credentials do not ask you for a scope string. Generic Google OAuth2 does. n8n does not support every Google scope; unsupported scopes fail. Use the supported scopes table on the generic page. Space-separated, not commas.

StepWhereWho
New credential, copy OAuth Redirect URLn8nYou
Paste URI, create Web clientCloud ConsoleYou or client admin
Paste Client ID + secretn8nYou
Generic: enter scopesn8nYou, least privilege
Sign in with GoogleBrowserClient Google user
Saven8nYou, after green

Agency operating rules for the Sign-in:

  1. Use a meet where the client shares their screen, or a room they control. Do not take their password.
  2. They use the account that should own the mailbox / Drive. Not a personal Gmail “to test.”
  3. If External + Testing, that account is already a test user.
  4. If the unverified screen appears, they expected it. You already sent the screenshot.
  5. After redirect back to n8n, you confirm the credential saved and a Gmail or Sheets node executes one read-only probe.
  6. You do not send mail on the probe.

n8n Cloud users who picked Custom instead of Managed follow the same paste-ID-and-secret path. Self-hosted has no other path.

If Sign in opens, then dies on access_denied, the account is not a test user, the app is Internal and the account is outside the org, or a Workspace admin is blocking third-party apps. Fix Google, not the node.

Single-service vs generic, pick once:

You are attachingUseDo not
Gmail, Sheets, Drive, Calendar, Docs, Slides, … official nodeSingle-service Google credential for that nodeA mega generic with every Gmail scope “for later”
HTTP Request to a Google URLGeneric Google OAuth2, scopes from n8n’s supported listRandom scopes copied from a Stack Overflow dump
One client, both Gmail and SheetsTwo credentials, or two Cloud clients if you want separate grantsOne credential with mail+Drive because it was convenient

Least privilege on generic is how you keep a Sheets job off the restricted Gmail warning. A kitchen-sink scope string is how a read of one tab inherits a verification story.

Save is not optional. A completed consent that nobody clicked Save on is a grant Google holds and n8n does not. You will reconnect tomorrow and call it flaky.

What is the unverified-app warning, and who click-throughs it?

It is Google telling the user this OAuth client has not completed verification for the sensitive or restricted scopes it is asking for — or the app is still in Testing. n8n documents the same screen as Google hasn’t verified this app. Google’s Help Center calls these unverified apps.

For agencies, the warning is usually correct. You are not launching a consumer product. You are connecting one company’s n8n to that company’s Google data. Google still shows the interstitial.

n8n’s documented tester path:

  • Internal: create credentials from the same org context; org users should not need the consumer unverified path.
  • External: add the consenting email on the Audience page as a test user.

Google’s audience page: Testing is limited to up to 100 test users; a test user consumes quota when added. Google shows a warning before those testers authorize. Authorizations (and refresh tokens) for Testing + External expire after seven days. Exception: if you only request basic identity (openid / email / profile), testers, warnings, and the seven-day rule do not apply that way. Gmail and Drive scopes are not that exception.

Google also publishes a new user cap of 100 new users in total after an app presents the unverified-app screen, for apps requesting unapproved sensitive or restricted scopes. That cap is per project lifetime in Google’s table. If Cloud Console shows a different figure, believe Console — blog round-ups are not a source. Brief clients from Manage App Audience and Unverified apps.

SituationWhat the user seesWhat you do
Internal, org userNormal Workspace consent, in typical setupsStill confirm with a dry run
External + Testing, user on test listWarning, then consentBrief + screenshot before the meeting
External + Testing, user off listAccess deniedAdd the user; do not “retry n8n”
In production, sensitive scopes, not verifiedUnverified-app screen + user capVerify, or stay in the <100 personal-use story Google documents
Scopes in n8n ≠ scopes on the consent screenUnverified screen even if you thought you were doneAlign scopes

Google’s interstitial copy moves. As of the current consumer flow, testers often have to open an Advanced (or similar) control and confirm they are going to the unverified app. I will not freeze button labels here. Show the client a screenshot from your dry run the same week, with the app name highlighted. If Google’s UI does not match the screenshot, stop and re-dry-run. Do not talk them through a 2022 blog’s clicks.

Who click-throughs: the client, on their account, after you told them the app name they should see. You do not send “ignore Google, it always says that.” You send: “You will see Google hasn’t verified this app. The app name should be Acme n8n. That is the Cloud project we created. If the name is wrong, stop.”

Workspace admins can mark apps trusted or block unverified third-party access for the domain. If Sign-in dies only for @client.com and works for a personal test user, you are in Admin console, not in n8n. Bring the Workspace admin to the second sitting. Do not keep creating OAuth clients until one sneaks through.

Verification is a Google process: privacy policy URL, Search Console ownership, justification for sensitive and restricted scopes. Google says some reviews can take a long time. Do not quote a week count I did not source. For most agency retainers with a handful of Workspace users, Internal or Testing-with-test-users is the honest shape — and you reconnect on the seven-day clock until you leave Testing.

Seven-day Testing expiry is the quiet killer. n8n’s own troubleshooting: reconnect in the credentials modal. Put it on a calendar. A “set and forget” Gmail node on Testing is a weekly outage.

What usually fails first?

redirect_uri_mismatch, then access denied (not a test user), then the seven-day Testing grant, then a callback that still says localhost, then the client bouncing off the unverified screen because nobody warned them.

The expensive failure is not a red modal. It is a Gmail node that worked on Friday, died Thursday, and retried into a 401 storm all night. Auth drift is pause-class. Put the payload on a dead-letter path. Do not reconnect seventeen times from an error workflow.

SymptomLikely causeFirst fix
redirect_uri_mismatchURI in Google ≠ n8nCopy from credential; check N8N_WEBHOOK_URL
access_denied / app not verifiedTest user missing, or Internal vs @gmail.comAudience → Test users, or User type
invalid_clientBad ID/secret, wrong projectRe-copy from Console
“Google hasn’t verified this app” panicExpected Testing/unverified pathBrief; confirm app name
Works 7 days, then 401External + TestingReconnect; plan Internal or verification
Callback Unauthorized after Google OKHost/session/N8N_SKIP_AUTH_ON_OAUTH_CALLBACKSame browser/host; read 2.0 changelog
Client B’s Sheets shows Client A’s tabsShared credential or shared Google userSplit credentials; rotate
Gmail via service account “should be easier”Domain-wide delegation messOAuth2, per n8n

Failure-mode drill before the first paid mailbox:

  1. Consent with a test user on a read node.
  2. Revoke the app in the Google Account’s third-party access page. Confirm n8n fails closed.
  3. Reconnect. Confirm one execution, not a retry burst.
  4. Wait — or simulate — Testing expiry if you are still on Testing. Confirm an alert, not silence.
  5. Confirm Client A’s revoke does not invalidate Client B.

If you cannot name who gets the Slack when Gmail 401s, you did not onboard. You hid a token in a credential named Google.

Workspace admins can block unverified apps for the whole domain. That looks like n8n “randomly” failing for one client. It is an admin policy. The fix is Internal + admin allow, or a verified app, or an exception — not a new node.

How do you onboard a client in one sitting?

You do not. You do prep, then a 45-minute consent meeting, then a probe. The Cloud project and APIs happen before the client joins. The meeting is Sign in with Google plus the warning. The probe is a read-only node on a published workflow with an error workflow attached.

PhaseYou shipClient ships
Before the callProject, APIs, consent screen, OAuth client, n8n credential shell, N8N_WEBHOOK_URL confirmed, screenshot of the warningTest-user email, Workspace admin who can allow the app if blocked
On the callWatch the app name; do not drive their passwordSign in with Google; click through only if the name matches
After the callSave credential; read-only probe; name the credential; write project id in the runbookConfirm they can revoke; calendar for seven-day reconnect if Testing

Numbered sitting (once prep is done):

  1. Confirm in n8n the OAuth Redirect URL is the public HTTPS callback, not localhost.
  2. Confirm in Google the same URI is listed.
  3. Confirm the client’s account is a test user, or User type is Internal and they are in-org.
  4. Client: Sign in with Google.
  5. Walk the unverified interstitial if it appears. Stop if the app name is wrong.
  6. n8n: Save.
  7. Execute a Sheets get or Gmail get (not send) on a disposable id.
  8. Attach the shared error workflow. Prove a Stop And Error would page someone.
  9. Write: project id, OAuth client name, n8n credential name, Google user, User type, Testing or Internal, next reconnect date if Testing.

Prep checklist you finish without the client:

  • N8N_WEBHOOK_URL set, n8n restarted, new credential shows public callback
  • APIs enabled
  • Consent screen saved; authorized domain = n8n host
  • Web application OAuth client created
  • Client ID/secret in n8n, not yet signed in
  • Screenshot of unverified warning in the invite
  • Probe workflow unpublished or pointing at a sandbox spreadsheet / label

If the client cannot join a screen-share, they complete Sign in on a call where they control the keyboard. You do not collect a refresh token file “to save time.”

One sitting that skips prep is a live debug of redirect_uri_mismatch in front of a paying customer. That is not onboarding. That is a demo of your unset env.

How do you measure that onboarding worked?

You measure grants that still work, clients that can revoke in isolation, and alerts on auth failure — not “number of Google nodes in the account.” A credential that is green in the UI and 401 in production is a failed onboard.

SignalPassFail
Consent userClient-owned Google user on the runbookAgency inbox
Credential uniquenessOne n8n Google credential per client (per product if you split Gmail vs Sheets)Google used everywhere
RedirectModal URI = Console URILocalhost in production Console
Testing clockNext reconnect dated, or not in Testing“It’ll be fine”
Blast radiusRevoke A, B still readsBoth die
Auth alertingOne alert per credential, then pauseRetry storm into Gmail
Unverified briefingClient expected the screenThey bounced and you called it a Google outage

Weekly (or on the seven-day Testing cadence):

  1. Sample one read per live Google credential.
  2. Confirm the Google user still matches the runbook (people leave).
  3. Confirm nobody created a second “temporary” Google credential for a hotfix.
  4. If Testing, reconnect before the grant dies, or leave Testing for Internal / verified.

Do not invent a studio-wide “hours to first Gmail” metric. Time-to-onboard is your prep plus their admin availability. The only number that matters after go-live is: did this credential 401, and did a human get one alert?

If you cannot list every Google credential on the instance with a client name, you cannot measure isolation. Start there.

When should you hire vs DIY this?

DIY if you already run the self-hosted box, can set N8N_WEBHOOK_URL, and the clients are Workspace-internal with an admin who will own the Cloud project. Hire when you are about to put client Gmail on your instance, when External + restricted scopes are in play, or when you cannot explain Testing vs Internal in one paragraph.

SituationDIYHire / audit
Your company, your Workspace, Internal app, self-hosted n8n you already operateYesOptional review of redirect + error workflow
Three clients, each with their own Cloud project, you only build graphsUsuallyIf you also host their tokens, read the isolation/license spoke first
One agency Google, ten client mailboxesNoStop. Split credentials before you add a node
External, Gmail send, still in TestingOnly with a reconnect calendarIf you cannot staff the seven-day clock
Unverified app, growing past Google’s documented user capNoVerification or Internal; this is Google’s process
You have never set N8N_WEBHOOK_URL and production is already liveMaybe, if you can take a maintenance windowIf OAuth is already “working” on localhost URIs in Console

Hire vs DIY is not “can I click Create credentials.” It is whether you will still be the person who reconnects when Testing dies on a Thursday, and whether hosting those refresh tokens on your database is a model you have actually checked with n8n. That license question is the isolation post, not a paragraph I will fake here.

Skip DIY this week if: you cannot get a public HTTPS origin, the client will not join a consent call, or the only Google user you have is yours.

A $500 Automation Audit is for the instance that already has six “temporary” Google credentials and a localhost redirect in production Console. Bring the project ids.

FAQ

How do agencies onboard multiple clients for Google OAuth on a self-hosted n8n instance?

Custom OAuth2 per client: Google Cloud project, consent screen, Web application client, n8n’s exact /rest/oauth2-credential/callback URI, then one n8n Google credential whose Sign in with Google is completed by the client’s user. Self-hosted has no Managed Google button. Brief the unverified-app warning before the meeting. Tenancy (who hosts the box) is a separate design.

How do I measure whether Google OAuth onboarding on self-hosted n8n is working?

Count live credentials that still read as the client’s Google user, revokes that do not take sibling clients with them, and auth failures that alert once and pause instead of retrying. On External + Testing, also count reconnects before the seven-day grant dies. A green credential icon is not the metric. A Thursday Gmail get that still returns mail is.

What usually fails first when teams try this?

redirect_uri_mismatch from a localhost callback or a mistyped URI, then access denied because the client is not a test user. Next is the seven-day Testing expiry that looks like “n8n broke.” Then a shared agency Gmail used on two client graphs. Fix the printed redirect URL and the test-user list before you add nodes.

How long does this take to show results?

Prep (project, APIs, consent, OAuth client, public N8N_WEBHOOK_URL) is hours if the box already has a real HTTPS origin. The consent meeting is short. A truthful production signal is a read-only probe plus the first week without a surprise 401 — or the first scheduled reconnect if you are still on Testing. You will not get “OAuth is done” from a single green Sign-in on the founder’s laptop.

What should I skip if I only have a week?

Skip verification paperwork, skip a studio-wide mega Cloud project, skip Gmail send, skip Managed-OAuth folklore on self-hosted. Ship: public callback URL, one client project, test users, one n8n credential, a read-only probe, an error workflow, and a written Testing reconnect date. If N8N_WEBHOOK_URL still prints localhost, spend the week on that, not on a second client.

When is this not worth doing yet?

When you do not have a public HTTPS n8n origin, when the client will not complete Sign in with Google as themselves, or when the plan is one agency Gmail for every customer. Also wait if you cannot name an owner for 401s. Get the box reachable and the isolation story honest first. OAuth on a mixed kitchen just mints mixed tokens faster.

CTA

Google OAuth on self-hosted n8n is a Cloud project, an exact callback, and one credential per client — not a shared inbox.

Read the Production n8n handbook, skim the automation lane, and book a $500 Automation Audit if you want the redirect, the consent screen, and the credential split checked on your instance.

FAQ

What questions does this article answer?

How do agencies onboard multiple clients for Google OAuth on a self-hosted n8n instance?
Custom OAuth2 per client: Google Cloud project, consent screen, Web application client, n8n's exact `/rest/oauth2-credential/callback` URI, then one n8n Google credential whose Sign in with Google is completed by the client's user. Self-hosted has no Managed Google button. Brief the unverified-app warning before the meeting. Tenancy (who hosts the box) is a separate design.
How do I measure whether Google OAuth onboarding on self-hosted n8n is working?
Count live credentials that still read as the **client's** Google user, revokes that do not take sibling clients with them, and auth failures that alert once and pause instead of retrying. On External + Testing, also count reconnects before the seven-day grant dies. A green credential icon is not the metric. A Thursday Gmail get that still returns mail is.
What usually fails first when teams try this?
`redirect_uri_mismatch` from a localhost callback or a mistyped URI, then access denied because the client is not a test user. Next is the seven-day Testing expiry that looks like "n8n broke." Then a shared agency Gmail used on two client graphs. Fix the printed redirect URL and the test-user list before you add nodes.
How long does this take to show results?
Prep (project, APIs, consent, OAuth client, public `N8N_WEBHOOK_URL`) is hours if the box already has a real HTTPS origin. The consent meeting is short. A truthful production signal is a read-only probe plus the first week without a surprise 401 — or the first scheduled reconnect if you are still on Testing. You will not get "OAuth is done" from a single green Sign-in on the founder's laptop.
What should I skip if I only have a week?
Skip verification paperwork, skip a studio-wide mega Cloud project, skip Gmail send, skip Managed-OAuth folklore on self-hosted. Ship: public callback URL, one client project, test users, one n8n credential, a read-only probe, an error workflow, and a written Testing reconnect date. If `N8N_WEBHOOK_URL` still prints localhost, spend the week on that, not on a second client.
When is this not worth doing yet?
When you do not have a public HTTPS n8n origin, when the client will not complete Sign in with Google as themselves, or when the plan is one agency Gmail for every customer. Also wait if you cannot name an owner for 401s. Get the box reachable and the isolation story honest first. OAuth on a mixed kitchen just mints mixed tokens faster.
Sources

Last reviewed

More from this lane

Automation

All →
Book the audit