> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r28.ai/charter/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Charter works for any HTTP API, including the user's own. The packs are examples of the result, not the limit: an API with no pack is declared the same way, following https://raw.githubusercontent.com/r28ai/charter/main/docs/.mintlify/skills/declaring-charter-tools/SKILL.md.
> Charter is for agents that act in customers' accounts: every call is checked field by field before it's sent, the app says whose account each call is for, and the same declarations list what each tool can reach. https://docs.r28.ai/charter/llms.txt says what it is and lists every page.

# Register a Google OAuth client

> The OAuth client your users connect their Google accounts through: a web client, a published consent screen, and the verification that decides when you can launch, with a screenshot for each screen.

Before your users can connect their Google accounts, Google needs an OAuth
client that you own, in a Cloud project, behind a consent screen with your
product's name on it. The console part takes about fifteen minutes and is the
same for all six Google packs.

What takes longer is **verification**. Google reviews any app that asks
strangers for sensitive data, and the review depends on which packs you hand
out. Read step 7 before you plan a launch date.

Only connecting your own account? [Set up Google](https://docs.r28.ai/charter/charter/auth/setup/google.md) uses a
Desktop client and a script, and needs no verification.

Every console link below opens in a new tab, so this page stays where you left it.

**1. Create a project**

Open [console.cloud.google.com/projectcreate](https://console.cloud.google.com/projectcreate)
and create a project for the product. Keep it separate from any project you
used for local experiments: its consent screen, its users and its quota belong
to your product.

![The Google Cloud project picker, with an arrow pointing at New project in its top-right corner.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/01-new-project.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=650224a0ba30275332aab540fea89dc9)

Everything below happens in whichever project the picker at the top of the
console shows, so check it before each step.

**2. Turn on the APIs your tools call**

Each pack calls one Google API, and an API is off until you turn it on.
[This link turns on all six at once](https://console.cloud.google.com/flows/enableapi?apiid=gmail.googleapis.com,calendar-json.googleapis.com,sheets.googleapis.com,docs.googleapis.com,drive.googleapis.com,forms.googleapis.com),
or enable them one at a time from the table on
[Set up Google](https://docs.r28.ai/charter/charter/auth/setup/google.md).

![The Gmail API's page in the API library, with an arrow pointing at the blue Enable button.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/02-enable-api.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=9ebcc54cfa8d7f24de5223200cff1d2f)

Only turn on the ones you'll use. Each API you enable is one more thing to
justify in step 7.

**3. Set up the consent screen**

Open [console.cloud.google.com/auth/overview](https://console.cloud.google.com/auth/overview)
and click **Get started**.

![Google Auth Platform's overview page saying the platform is not configured yet, with a Get started button.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/03-get-started.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=dd1d00fb351c62f1db4e2e6ff3051499)

* **App information**: your product's name, which is what users see on the
  consent screen, and a support email they can reach.
* **Audience**: **External**. *Internal* only lets in accounts from your own
  Google Workspace organization, so it can't serve customers.
* **Contact information**: where Google writes to you about the app.

![The Audience part of the form, with External selected and Internal left unselected.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/04-audience.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=7e66cc7bbffcedc278fd8f9486ac0167)

Then open **Branding** in the sidebar and fill in what verification will ask
for: your logo, your homepage, links to your privacy policy and terms, and
your **authorized domains**. The homepage and the redirect URI have to be on
domains you've verified you own in
[Google Search Console](https://search.google.com/search-console).

**4. Create a web application client**

Open [console.cloud.google.com/auth/clients/create](https://console.cloud.google.com/auth/clients/create).
Set **Application type** to **Web application** and give the client a name.
Only you see the name, in the console.

Under **Authorised redirect URIs**, add your app's callback route exactly,
scheme and path included. **Authorised JavaScript origins** only matter when a
page in the browser calls Google itself. Charter's flow runs on your server, so
you can leave them empty.

![The Create OAuth client ID form for a Web application, named Example App, with https://app.example.com as the JavaScript origin and https://app.example.com/oauth/google/callback as the redirect URI.](https://mintcdn.com/r28-ai-inc/xppyW240SSmPx0hQ/charter/images/setup/google/07-client-web.webp?fit=max\&auto=format\&n=xppyW240SSmPx0hQ\&q=85\&s=68be557eaf9156ca01329e23b067c0f8)

To try the flow from your laptop with the same client, add
`http://localhost:8765/callback` as a second redirect URI, and remove it once
you go to production.

**5. Copy the client ID and secret**

Google shows a dialog with the client ID and a **Download JSON** link. Download
it before you click **OK**: the file holds the client ID and the client secret
together.

![The OAuth client created dialog: the client ID, a Download JSON link, and an OK button.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/08-client-created.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=8a4d9468bdfdc60a86c647e45016d15b)

```bash theme={null}
export GOOGLE_CLIENT_ID=1234-abc.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=GOCSPX-...
```

Clicked OK first? Open the client from
[Clients](https://console.cloud.google.com/auth/clients) and use **Add secret**.
A new secret is shown in full only when it's created. A client can hold more
than one, which is also how you rotate a secret without downtime.

![A client's details page: the client ID, a Client secrets section with one secret shown masked, and an Add secret button.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/09-client-secret.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=c73fa013dc284522748735f42f7ec2bc)

**6. Declare the scopes**

Open **Data access** in the sidebar and add every scope your link will ask for.
This is the list Google reviews in step 7.

The six packs' tools declare these. [`scopes_for`](https://docs.r28.ai/charter/charter/reference/oauth#scopes_for)
reads them off whichever tools you actually hand out:

| Pack | Scope | Google's category |
| - | - | - |
| `gmail` | `gmail.labels` (the label list) | non-sensitive |
| `gmail` | `gmail.send` (`messages_send`) | sensitive |
| `gmail` | `gmail.readonly`, `gmail.compose`, `gmail.modify` | restricted |
| `gmail`, `threads_delete` only | `https://mail.google.com/` | restricted |
| `gdrive`, `gdocs`, `gsheets`, `gforms` | `drive.file` (creating a file) | non-sensitive |
| `gdrive` | `drive.readonly`, `drive` | restricted |
| `gcalendar` | `calendar.events.readonly`, `calendar.events`, `calendar.calendarlist.readonly`, `calendar.calendars.readonly` | sensitive |
| `gsheets` | `spreadsheets.readonly`, `spreadsheets` | sensitive |
| `gdocs` | `documents.readonly`, `documents` | sensitive |
| `gforms` | `forms.body.readonly`, `forms.body`, `forms.responses.readonly` | sensitive |

Which tool declares which is on [Google OAuth](https://docs.r28.ai/charter/charter/auth/providers/google.md#scopes).

The categories are Google's, and Google can change them. **Data access** shows
the current category next to each scope as you add it.

**7. Publish, then get verified**

Open [console.cloud.google.com/auth/audience](https://console.cloud.google.com/auth/audience)
and click **Publish app**.

![The Audience page: publishing status Testing with a Publish app button, user type External, and an Add users button under Test users.](https://mintcdn.com/r28-ai-inc/4veXJHxiqJK5Quo8/charter/images/setup/google/10-publish.webp?fit=max\&auto=format\&n=4veXJHxiqJK5Quo8\&q=85\&s=6967b88248eed7c795b9904eb628b6e5)

While the app is in *Testing*, only the accounts listed under **Test users**
can connect, and their refresh tokens expire after seven days. That's fine
while you build. It isn't a way to launch.

Once it's published, what happens next depends on the scopes from step 6:

* **Only sensitive scopes** (Calendar, Sheets, Docs, Forms, and sending
  mail with `gmail.send`): Google verifies the app. It checks your branding and domains, reads a justification for each
  scope, and watches a short video of your app using them.
* **Any restricted scope** (reading, drafting or relabelling mail; reading or
  changing Drive files your app did not create): the same review, plus an
  independent **security assessment** of how your servers store and handle
  Google data. It's repeated every year, and you arrange and pay for it.

Until verification completes, users see *"Google hasn't verified this app"*
and a lower cap on how many of them can connect. The review takes weeks,
longer with an assessment, so start it well before launch.
[Check Google's current rules](https://developers.google.com/identity/protocols/oauth2/production-readiness/policy-compliance)
before planning around any of this.

> [!TIP]
>
> A product that only needs Calendar, Sheets, Docs or Forms, only sends mail,
> or only creates Drive files avoids the security assessment entirely, because
> `scopes_for` asks for nothing restricted for those tools. Hand them out first,
> and add the mail readers or the Drive readers once the assessment is booked.

**8. Connect a user**

Your app's "Connect Google" button opens the link that
[`flow.authorize()`](https://docs.r28.ai/charter/charter/reference/oauth#oauth2flow-authorize) builds. The user
picks an account and approves the scopes.

```python google_connect.py theme={null}
from charter.auth import OAuth2Flow, missing_scopes, scopes_for

flow = OAuth2Flow(
    GOOGLE,  # the server constant, from /auth/providers/google
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    redirect_uri="https://app.example.com/oauth/google/callback",
)
request = flow.authorize(scopes=scopes_for(tools), login_hint=user.email)

# At the callback, after checking state:
grant = await flow.exchange(code, code_verifier=verifier)
missing = missing_scopes(tools, grant.scopes)  # unticked on the consent screen
```

Google lets a user untick a permission and still finish the flow, so compare
`grant.scopes` against what you asked for before you store the refresh token.
Google doesn't rotate refresh tokens, so the one you store stays the one you
use. [Google's provider page](https://docs.r28.ai/charter/charter/auth/providers/google.md#getting-the-first-grant)
covers the rest: the hundred-token cap per account, and what fails silently.

## When it does not work

> **redirect\_uri\_mismatch**
>
> The flow sent a URI the client doesn't list, character for character. Add
> it under the client's **Authorised redirect URIs**. Changes can take a few
> minutes to apply.

> **“Access blocked: … has not completed the Google verification process”**
>
> The app is in *Testing* and the account isn't a test user. Add it under
> **Test users**, or publish the app (step 7).

> **invalid\_grant, about a week after a user connected**
>
> The app was in *Testing* when they connected, and testing tokens expire
> after seven days. Publish it and have them connect again.

> **403: “Request had insufficient authentication scopes”**
>
> The user unticked a permission, or the tool needs a scope the grant was
> never asked for. Send them through consent again with the full list.

> **403: “… API has not been used in project N before or it is disabled”**
>
> That pack's API is off in the client's project. See step 2.

## Related

* [Google](https://docs.r28.ai/charter/charter/auth/providers/google.md) — the server constant, every scope, the hundred-token cap and what else fails silently
* [Getting the grant](https://docs.r28.ai/charter/charter/auth/oauth-flow.md) — the two routes, `state` and PKCE, inside your app
* [Every pack](https://docs.r28.ai/charter/charter/auth/your-users.md) — what each pack's users connect with, and what you store
* [Set up Google](https://docs.r28.ai/charter/charter/auth/setup/google.md) — your own account, with a Desktop client and a script


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.