Managed OAuth vs Generic OAuth for Gmail REST (HTTP Request) – Which approach for production?

Hi everyone,
I’m building a production email agent with n8n Cloud and I’d like to get feedback from people who have already deployed Gmail integrations at scale.
Current architecture
I’m not using the Gmail node.
Instead, I’m using:
HTTP Request nodes
Gmail REST API
Generic Google OAuth2 credential
only one scope:
https://www.googleapis.com/auth/gmail.modify
The workflow performs:
list unread messages
get message
create draft
send message
mark as read
Everything works correctly.
Why I avoided the Gmail node
From what I understand, the Gmail node requests several fixed scopes including:

gmail.modify
gmail.compose
etc.
I wanted to follow the principle of least privilege and request only gmail.modify.
That’s why I switched to HTTP Request + Generic OAuth2.
My question about Managed OAuth
I’m now evaluating Managed OAuth available on n8n Cloud.
The documentation explains that it simplifies authentication, but I couldn’t find technical details about what actually happens behind the scenes.
I’d like to know:
Does Managed OAuth internally use the same Gmail credential (same scopes) as the Gmail node?
If I create a Managed OAuth Gmail credential, can I use it inside HTTP Request nodes?
During Google consent, which scopes are actually requested?
Is there any way to limit Managed OAuth to only:
gmail.modify
instead of the full Gmail scopes?
Has anyone successfully deployed a production Gmail REST workflow using Managed OAuth instead of a Generic OAuth credential?
Google verification
One thing I’m also trying to understand:
Does Managed OAuth change anything regarding Google’s OAuth verification requirements (restricted scopes, security assessment, etc.)?
I’m not asking for legal advice, just wondering whether anyone has real production experience with this.
Any feedback or production experience would be greatly appreciated.
Thanks!

Describe the problem/error/question

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version: cloud
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app): cloud
  • Operating system:

Hi @Jidenkaes

Your existing setup — Generic OAuth2 + HTTP Request + single gmail.modify scope — is the right call for a production Gmail agent where least-privilege matters. Managed OAuth is not designed for scope customization or HTTP Request node usage, and switching to it would likely expand your scope surface, not reduce it. Stick with your current architecture and publish your GCP app as Internal (if within a Google Workspace) to avoid the public verification requirement entirely .

This should give you a clearer picture

Hi @Jidenkaes
Managed OAuth is the same Gmail OAuth2 API credential the Gmail node uses, running on n8n’s own Google app. n8n deletes the scope field on managed credentials before the flow starts, so consent always requests the scopes pre-registered on that app, the full six including https://mail.google.com/, and no UI setting changes that. The OAuth client is n8n’s, so on verification there is no project of yours to submit.
It can be attached to HTTP Request nodes, Authentication set to Predefined Credential Type and Credential Type Gmail OAuth2 API, but it carries those six scopes with it.
A Custom Scopes toggle for the Gmail credential was merged on 22 July and isn’t in a release yet (2.32.3 doesn’t have it). It applies to custom OAuth2 credentials only, since managed ones get the scope stripped, so once it ships you can run the Gmail node itself on gmail.modify alone.

For production, choose the path that gives you controlled credentials, refresh-token handling and a clear revocation process. Test token expiry and reconnect behaviour in a separate account before deciding because the happy-path request looks similar in both setups.