Skip to content

Connect Gmail

kadyapam edited this page Oct 17, 2026 · 4 revisions

Connect Gmail

Run playbooks/gmail-triage/triage.yaml against your real inboxes, one or more Gmail accounts, read-only.

Needs noetl 5.3.0 or newer: noetl credential and google_oauth auth arrived in 5.2.0 (noetl/cli#96), and the playbook's thinking: disabled cost control in 5.3.0 (noetl/cli#97).

How it fits together

flowchart LR
    A[Claude<br/>framework: claude] -->|list_unread account| L[tools/list_unread.yaml]
    A -->|get_message account, id| G[tools/get_message.yaml]
    L & G -->|"http, auth: google_oauth<br/>credential: gmail-&lt;account&gt;"| B[noetl bridge]
    B -->|refresh token → access token| T[(oauth2.googleapis.com)]
    B -->|Bearer access token| M[(Gmail API, read-only)]
    S[~/.config/noetl/credentials/gmail-&lt;account&gt;<br/>mode 600] -.-> B
Loading
  • One credential per account, named gmail-<account> (gmail-work, gmail-personal, …). It holds a refresh token for the scope gmail.readonly.
  • On each run, NoETL exchanges it for a one-hour access token, cached in-process. Neither token ever enters playbook variables or step results.
  • Claude sees only what the two tools return: message ids, then From / Subject / Date / snippet. It never sees bodies.

1. Create a Google OAuth client (once)

You need one OAuth client, shared by all your accounts.

  1. Go to console.cloud.google.com and create a project, e.g. noetl-mail.

  2. Open APIs & Services → Library, search for Gmail API, and click Enable.

  3. Open Google Auth Platform (formerly the "OAuth consent screen"):

    • Branding: an app name such as NoETL mail triage and your email.
    • Audience: User type External. Under Test users, add each Gmail address you'll connect.
    • Data access: add the scope .../auth/gmail.readonly.
  4. Under Clients → Create client, choose application type Desktop app, then click Download JSON.

  5. Save the file privately:

    install -d -m 700 ~/.config/noetl && install -m 600 ~/Downloads/client_secret_*.json ~/.config/noetl/google-client.json

Testing vs. production. While the app's publishing status is Testing, Google expires its refresh tokens after 7 days, so you'd re-authorize weekly. For a long-running personal setup, set Audience → Publishing status → In production. The consent screen will then say Google hasn't verified the app; choose Advanced → Go to … (unsafe) for your own accounts. Unverified apps are limited to 100 users, which is fine for personal use. A Google Workspace account can use user type Internal instead, which avoids both the warning and the expiry.

2. Authorize each account (once per account)

noetl credential google gmail-work --client-secret-file ~/.config/noetl/google-client.json --login-hint you@work.example

Your browser opens Google's consent page. Pick the account and allow read-only Gmail access. The terminal then confirms which mailbox it stored:

Stored google_oauth credential `gmail-work` for you@work.example at ~/.config/noetl/credentials/gmail-work (mode 600); scopes: https://www.googleapis.com/auth/gmail.readonly

Repeat for every account:

noetl credential google gmail-personal --client-secret-file ~/.config/noetl/google-client.json --login-hint you@gmail.com

On a machine without a browser, add --no-open and open the printed URL yourself. The redirect goes to 127.0.0.1, so finish on the same machine or forward the port.

Check what's stored (values are never shown):

noetl credential list

3. Store the Anthropic key in the same place

noetl credential set anthropic

Paste the sk-ant-api… key at the hidden prompt. Once this is stored, no environment variable is needed, including under systemd.

4. Run it

The account names in accounts= are the part after gmail-:

noetl run playbooks/gmail-triage/triage.yaml --runtime local --set accounts=work,personal,family --verbose

With --verbose you'll see one list_unread call per account, then a batch of get_message calls, then the report. Without credentials for an account, that account's tool call fails, and Claude says so and carries on with the others.

Cost. Measured on four real inboxes (27 messages, 3 turns):

setup input cache write / read output approx. per run
Sonnet, 3 days, all tabs, raw Gmail JSON 87,715 — 6,148 ~$0.35
Haiku, thinking: disabled, Primary tab, last day, compacted tool output, prompt caching (the playbook default) 8 8,158 / 2,126 2,667 ~2–3¢

What drives it: the number of messages (each get_message result goes back to the model), the size of each result (the compact rhai steps cut it to sender/subject/date/snippet), and the model. To trim further, lower max_results or narrow query in tools/list_unread.yaml. thinking: disabled and prompt caching need noetl 5.3.0; on older releases set --set model=claude-sonnet-5-5, because Haiku can spend the summary turn on reasoning and return nothing.

5. Every morning

cp systemd/gmail-triage.* ~/.config/systemd/user/ && systemctl --user daemon-reload && systemctl --user enable --now gmail-triage.timer

Edit --set accounts= in gmail-triage.service to your account names. Read the result with journalctl --user -u gmail-triage.service.

Revoking access

  • One account: remove the app at myaccount.google.com/permissions, then delete ~/.config/noetl/credentials/gmail-<account>.
  • Everything: delete the OAuth client in Google Cloud. All refresh tokens issued to it stop working.

Related

Configuration Reference § http auth · Security · Troubleshooting

Clone this wiki locally