Skip to content
Skip to main content

Gmail API OAuth scopes reference

Last updated:

Pick the wrong Gmail scope and Google makes you pay for it before launch. Ask for gmail.modify when you only read mail and you’re routed into a CASA Tier 2 security assessment that can take weeks and cost real money. Ask for too little and the first send returns 403 insufficient authentication scopes. This page lists the exact Gmail API OAuth scope strings Nylas requests for each email, calendar, and contacts feature, so you grant the narrowest set that still works.

For the Google and Microsoft scope strings side by side, see OAuth scopes for email and calendar. This page is the Google-only deep reference, including the verification tiers that decide your launch timeline.

What OAuth scopes do I need for reading and sending email through the Gmail API?

Section titled “What OAuth scopes do I need for reading and sending email through the Gmail API?”

Reading mail needs the https://www.googleapis.com/auth/gmail.readonly scope, and sending needs https://www.googleapis.com/auth/gmail.send. Google classifies gmail.readonly as restricted and gmail.send as sensitive, so a read-and-send app crosses both of Google’s two review tiers at once. Nylas requests these exact strings when you call GET /v3/grants/<NYLAS_GRANT_ID>/messages and POST /v3/grants/<NYLAS_GRANT_ID>/messages/send.

Google scopes are full URIs prefixed with https://www.googleapis.com/auth/, and there’s no shorter form. The table below maps each common task to the scope Nylas requests, verified against the Google tab in the granular scopes reference. The Wider scope column shows the broader scope Google also accepts when a feature needs write access, which the modify and folder operations require.

FeatureRequired Gmail scopeWider scope that also works
Read messages and attachments/gmail.readonly/gmail.modify
Update or delete messages/gmail.modifyhttps://mail.google.com/ (hard-delete)
Send email/gmail.send/gmail.compose, /gmail.modify
Read and write drafts/gmail.compose/gmail.modify
Manage folders (labels)/gmail.labels/gmail.modify
Read calendars and Free/Busy/calendar.readonly/calendar
Create or update events/calendar.events/calendar
Read contacts/contacts.readonlyNone
Read and write contacts/contactsNone

One classification catch: gmail.compose and gmail.insert are restricted scopes that trigger the CASA assessment, just like gmail.readonly and gmail.modify, so neither is a lighter way to dodge the restricted-scope review. gmail.send is sensitive and gmail.labels is non-sensitive. Mail, calendar, and contacts scopes are independent in Google, so a calendar-only app never requests a gmail.* scope. Request gmail.send even for scheduled delivery: the scheduled send feature queues the message but still sends through your Gmail send scope.

What are common OAuth errors when integrating Gmail API in a Node.js app?

Section titled “What are common OAuth errors when integrating Gmail API in a Node.js app?”

The four errors that trip up most Node.js Gmail integrations are access_denied, redirect_uri_mismatch, invalid_scope, and 403 insufficient authentication scopes. The first three surface during the consent redirect, before any grant exists, and the 403 surfaces at the first API call when the granted scopes don’t cover the operation you attempted.

access_denied almost always means your OAuth app is still in testing mode or the requested restricted scopes aren’t verified yet, which blocks any account outside your test-user list. redirect_uri_mismatch means the callback URL in your auth request doesn’t byte-match the one registered in your Google Cloud project, down to the trailing slash. invalid_scope means a malformed or non-existent scope string, often a missing https:// prefix. The 403 means you asked for gmail.readonly but tried to modify a label. Resolve each through Fix Google access_denied OAuth errors and the broader troubleshoot OAuth errors guide, which covers all four against both providers.

What are the best practices for OAuth scope management in an email API integration?

Section titled “What are the best practices for OAuth scope management in an email API integration?”

The core practice is least privilege: request the smallest Gmail scope that covers your features, then widen only when a feature needs it. Restricted scopes like gmail.readonly and gmail.modify each trigger Google’s annual security assessment, so dropping one unused restricted scope can save weeks of review and remove an audit cost from your launch plan.

Three habits keep a Gmail integration clean. First, separate read from write: use gmail.readonly over gmail.modify wherever your app never edits or deletes mail, since the read scope alone often satisfies the assessment with a lighter review. Second, use incremental authorization, requesting send access the first time a user clicks “send” rather than on day one, which raises consent conversion. Third, set scopes on the Nylas connector once rather than per call, so the hosted flow at GET /v3/connect/auth always sends the same vetted set. The table in the next section maps these habits to concrete app shapes.

Choose the least-privilege Gmail scope set

Section titled “Choose the least-privilege Gmail scope set”

Request the smallest scope that covers your features, then add scopes only when a feature needs them. This keeps Google off the restricted-scope assessment track where possible, since fewer restricted scopes mean a shorter review. The table maps 5 common app types to the minimal Gmail scopes each needs, from a single restricted scope for a read-only inbox up to four scopes for a mail-plus-calendar app.

App typeGmail and Google scopes
Read-only inbox/gmail.readonly
Send-only/gmail.send
Full email client/gmail.modify, /gmail.send
Calendar scheduler/calendar.events
Mail plus calendar/gmail.modify, /gmail.send, /calendar.events

Each row maps to a real product shape. A read-only inbox that classifies or indexes mail needs only gmail.readonly, because it never writes or sends, though that one scope still triggers a restricted-scope review. A full email client that marks messages read, moves them between labels, and sends replies needs gmail.modify for the modify and label operations plus gmail.send for outbound mail. A calendar scheduler that creates and updates events needs calendar.events and nothing from the gmail.* family. Drop scopes you don’t call, because every extra restricted scope lengthens the assessment.

How do I set Gmail scopes on the auth request?

Section titled “How do I set Gmail scopes on the auth request?”

You set Gmail scopes on the Nylas connector or pass them to the hosted OAuth URL, not on each API call. The hosted flow at GET /v3/connect/auth sends the connector’s scopes to Google, the user consents once, and the code exchanges for a grant at POST /v3/connect/token. Scope strings are space-separated in the scope query parameter.

The request below starts the hosted Google OAuth flow for a read-and-send app. It lists three Gmail and calendar scopes plus the offline access Google needs to return a refresh token, so the grant survives past the first access-token expiry. Always include the refresh-token request, or the grant stops working after about 60 minutes.

After the user consents, Google redirects to your callback with a code. Exchange it for a grant, then read mail through the unified endpoint. The call below lists messages from the connected Google account using the grant_id the token exchange returned, with a limit of 50 messages per page.

When should you call the Gmail API directly?

Section titled “When should you call the Gmail API directly?”

Call the Gmail API directly when your app is Google-only and depends on Gmail-specific features the unified schema doesn’t surface, such as raw RFC 822 history syncs through users.history.list or fine-grained label-color management. Google’s own client handles those edge cases natively, and a single-provider app pays no penalty for skipping the abstraction layer.

The calculation flips the moment you add a second provider. Supporting Outlook alongside Gmail through native APIs means a second, unrelated integration against Microsoft Graph, with its own consent model, ID formats, and throttling rules. One unified schema collapses both into a single code path, and the same grant reaches 6 providers, so adding iCloud or Yahoo later costs no new integration. The unified read call GET /v3/grants/<NYLAS_GRANT_ID>/messages returns the same JSON shape for every provider, which is the difference from mapping each provider’s response by hand.

TaskGmail API directlyUnified API with Nylas
Auth setupGCP project, OAuth consent screen, verification1 connector, OAuth handled for you
Read mailusers.messages.list with gmail.readonlyGET /v3/grants/<NYLAS_GRANT_ID>/messages
Send mailusers.messages.send with gmail.sendPOST /v3/grants/<NYLAS_GRANT_ID>/messages/send
Restricted-scope reviewYou manage CASA assessment yourselfSame scopes, one consent flow
Other providersGoogle onlyMicrosoft, iCloud, Yahoo, IMAP, and more

Beyond the read-versus-write split and the verification tiers covered above, a few Google-specific details change when these scopes take effect. Each of the four items below maps to a real failure mode developers hit during Gmail integration, and getting them wrong is a common reason a working integration starts returning 403.

  • Adding a scope forces re-authentication. Google only grants new scopes at consent time. Add gmail.send after a user already connected with gmail.readonly and their existing grant doesn’t gain it. The user re-authenticates through GET /v3/connect/auth to pick up the wider access.
  • Restricted scopes require an annual assessment. gmail.readonly, gmail.modify, gmail.compose, gmail.insert, and https://mail.google.com/ sit in Google’s restricted tier, which adds a yearly CASA security assessment on top of OAuth verification. gmail.send is a sensitive scope that needs OAuth verification but not the restricted-scope assessment, and gmail.labels is non-sensitive. Plan several weeks of lead time before production launch, not days.
  • Contacts sources change the scope. The contacts.readonly scope covers the personal address book, but the inbox and domain contact sources need contacts.other.readonly and directory.readonly. Request those only when you read recent-correspondent or directory contacts.
  • Hard-delete needs the widest scope. A DELETE on a message under gmail.modify moves it to Trash. Permanently deleting requires the full https://mail.google.com/ scope, which Google reviews most strictly of all.

The canonical list lives in the Google OAuth 2.0 scopes reference. Cross-check any scope against it and the Gmail API scopes page before you add it, because Google renames and re-tiers scopes over time.