Recipe: connect Google Workspace
This recipe connects Google with the google_workspace preset, so a board can read a sheet, add a row, read your calendar, see busy times, add a calendar event with no guests, list Drive files and read YouTube numbers while it works. It uses your own Google Cloud app (its client id and client secret). Read Connect a service that signs in with OAuth first for how OAuth connections, limits, requests and receipts work, and Let your board use a service while it works for connections in general.
What this connection can never do:
- No Gmail. It never asks for a Gmail scope, and BakedBrie refuses every Gmail path on every connection. To put approved emails in Gmail Drafts, use the Gmail draft destination in Draft invoice reminders in Gmail.
- No invites.
calendar_create_eventsendssendUpdates=noneand has no guests field, so the event has no one to email. Any other calendar event write is refused. Google's own page warns that "some emails might still be sent" withsendUpdates=none; with no guests on the event, there is no one to send them to. - No sharing. Drive sharing (adding a permission) makes Google email the new reader, so BakedBrie refuses it on every connection.
- Nothing wider than you tick. The connection asks Google for exactly the scopes of the operations you choose, computed for you. A wider set is refused (
CONNECTION_SCOPE_WIDER_THAN_OPERATIONS).
Before you run it
- In the Google Cloud console, pick or create a project and turn on the APIs for the operations you want: Google Sheets API, Google Calendar API, Google Drive API, YouTube Data API v3, YouTube Analytics API.
- Set up the OAuth consent screen. Choose the user type:
- Internal (only people in your Google Workspace organization can sign in): no Google verification and no 7-day limit. Best for a company on Google Workspace.
- External (any Google account, including personal Gmail): while the app's publishing status is Testing, refresh tokens expire after 7 days and at most 100 test users can sign in, so the connection asks you to "Sign in to Google again" every week. Once published but not verified, Google shows a warning screen at sign-in and caps the app at 100 users over its life, which is fine for one company. Sensitive scopes need Google's verification for an External app in production, and Restricted scopes also need a yearly security assessment.
- Create an OAuth client of type Web application and add this exact redirect URI (character for character; it is the same for every BakedBrie connection and destination):
https://app.bakedbrie.com/settings/destinations/oauth/callback
- Add the scopes of the operations you will tick on the consent screen's Data Access page (the table below lists them). Google shows each scope's class there.
- Note the client id. Put the client secret in your own shell, for example
export GOOGLE_CLIENT_SECRET=.... Never paste it into the chat. whoamimust showconnectionsandconnections_oauthon.
Operations and scopes
Tick only what the board needs. The create form ticks the three reads marked "default"; writes and Restricted operations are never ticked for you. The scopes are computed from your ticks and shown under the checklist. Every scope below starts with https://www.googleapis.com/auth/.
| Operation | What it does | Reads or writes | Scope | Google class |
|---|---|---|---|---|
sheets_read_range (default) | Up to 50 rows of one range, first 8 columns | read | spreadsheets.readonly | Sensitive |
sheets_append_row | Adds one row of up to 5 values after the last row of a range | write | spreadsheets | Sensitive |
calendar_list_events (default) | Up to 50 events on your main calendar between two dates (UTC): title, status, times | read | calendar.events.owned.readonly | not stated in Google's docs |
calendar_freebusy (default) | Busy blocks on your main calendar between two dates, no details | read | calendar.freebusy | not stated in Google's docs |
calendar_create_event | Adds an event with no guests to your main calendar; nobody is invited or emailed | write | calendar.events.owned | not stated in Google's docs |
drive_list_files | Up to 50 files this Google app created or a person opened with it | read | drive.file | Non-sensitive |
drive_get_file | One file's name, date and size, if this app can see it | read | drive.file | Non-sensitive |
drive_list_files_all | Up to 50 of all your Drive files: names, dates, sizes | read | drive.metadata.readonly | Restricted |
drive_get_file_any | Any file's name, date and size by its id | read | drive.metadata.readonly | Restricted |
youtube_channel_stats | Views, subscribers and video count of your channel | read | youtube.readonly | not stated in Google's docs |
youtube_video_stats | Views, likes and comments of one video | read | youtube.readonly | not stated in Google's docs |
youtube_analytics_report | Views and likes per day for your channel, up to 50 days | read | yt-analytics.readonly and youtube.readonly | not stated in Google's docs |
Notes:
- Drive.
drive.fileis the default and Non-sensitive, but Google then shows only files this Google app created or a person opened with it. BakedBrie has no file picker, so these two operations usually find little until your app has been used with the files. The_alland_anyvariants see every file's name and date, butdrive.metadata.readonlyis Restricted: an External app in production needs Google's yearly security assessment (an Internal app does not). Neither reads file contents. - Sheets. Values are added with
valueInputOption=RAW: Google stores them exactly as typed and never as formulas. BakedBrie also refuses a value that starts with=,+,-or@. A spreadsheet id and a range likeInvoices!A1:E1(a sheet name without spaces) are the inputs. - Calendar. Every operation uses your main (
primary) calendar. Event times you send need a time zone offset, like2026-10-01T09:00:00-04:00. - YouTube. Each Data API call uses 1 unit of your project's daily YouTube quota. Google now requires
youtube.readonlyfor Analytics reports too, soyoutube_analytics_reportasks for both scopes. - Where Google's docs state no class, check the Data Access page in your Cloud console; it shows the class when you add the scope.
What the board sees, and the taint rule
Answers are provider data, shown to agents only as untrusted facts. Anything a person typed is free_text: sheet cells, event titles and file names. A run that read a non-empty free_text field can only read in the same round (CONNECTION_WRITE_TAINTED), so "read a sheet, then add a row" in one agent round needs a person, or a flow where the write comes first or follows a read of numbers only. Busy times and YouTube numbers never taint.
Writes (sheets_append_row, calendar_create_event) run within limits a person sets in the web app, and BakedBrie sends each at most once. Google documents no idempotency key for these calls, so if BakedBrie cannot tell whether Google accepted a write, it never sends it again by itself: check the sheet or calendar before a person chooses Send again.
The prompt
In BakedBrie, connect Google Workspace to the board "{{BOARD_NAME}}". Use only the BakedBrie MCP tools. Start with whoami and stop if connections or connections_oauth is off. First read /docs/recipes/connections-oauth and /docs/recipes/google-workspace with read_docs.
My Google OAuth client id is {{CLIENT_ID}}. The client secret is in my environment variable GOOGLE_CLIENT_SECRET. The operations I want: {{OPERATIONS, for example sheets_read_range, sheets_append_row}}.
1. Call manage_connection with action presets, preset google_workspace and operations [my operations]. Show me the computed scopes and each scope's class. Stop if any is Restricted and I did not ask for it.
2. Call prepare_secret with kind google, purpose connection and env_var GOOGLE_CLIENT_SECRET. Run the command it returns in my shell exactly as given, then use the drop_id. Never print the secret.
3. Create it: manage_connection action create, name "Google", preset google_workspace, operations [my operations], client_id {{CLIENT_ID}}, credential {"drop_id": "<the drop id>"}, and a new request_id.
4. Call manage_connection action connect_oauth. Give me oauth.consent_url: only a person can finish signing in. I open it in the browser where I am signed in to BakedBrie, approve at Google, and click Finish connecting. Then call oauth_status until the grant is connected, or tell me its error code.
5. Attach it: manage_board_connection action attach, handle google, the operations, the agents and stages that may use it, and when_to_use.
6. Stop and tell me: "Only a person can let a board use a connection. Open the limits link and set limits." Suggest $0.00 per call, reads 10 per card and 200 per month, writes 1 per card and 20 per month.
A schedule example: YouTube numbers into a sheet
Tick youtube_analytics_report and sheets_append_row. With schedules, make a Monday card "Add last week's YouTube views to the sheet". The maker reads youtube_analytics_report for the last 7 days (numbers only, so the round stays untainted) and then asks for one sheets_append_row with the week and the total views. The write needs a limit a person set, and it lands at the end of the range you name.
Sign-in facts to know
- Access tokens last about an hour; BakedBrie refreshes them itself.
- A refresh token unused for 6 months stops working.
- Google keeps at most 100 refresh tokens per Google Account per OAuth client and silently drops the oldest, so signing in many times with one app can end an older sign-in.
- A password change ends sign-ins that hold Gmail scopes. This connection holds none.
- Adding or removing an operation changes the scopes, so the connection asks you to sign in again with exactly the new set. BakedBrie sends
include_granted_scopes=false, so a new sign-in never inherits scopes an older one held. - Disconnecting revokes the sign-in at Google (
https://oauth2.googleapis.com/revoke) and deletes BakedBrie's copy. - Provider events (Calendar and Drive push notifications) are not supported yet. Use a schedule.
Refusals you may see
| Code | What it means | Fix |
|---|---|---|
CONNECTION_SCOPE_WIDER_THAN_OPERATIONS | The definition asks for a scope no ticked operation needs. | Remove the scope, or tick the operation that needs it. |
CONNECTION_EMAIL_ENDPOINT_REFUSED | A path reaches Gmail. | Use the Gmail draft destination instead. |
CONNECTION_MESSAGE_ENDPOINT_REFUSED | A calendar write with guests or without sendUpdates=none, or a Drive sharing call. | Keep the preset's calendar_create_event as it is. |
OAUTH_SCOPE_WIDER_THAN_REQUESTED | Google granted more than BakedBrie asked for. | Sign in again; BakedBrie never accepts extra access. |