google-workspace

Comprehensive AI agent skill for all Google Workspace document operations — Docs, Sheets, Slides, Drive, Gmail, Calendar, Chat, Forms, Admin SDK, and Apps Script — via official REST APIs.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "google-workspace" with this command: npx skills add supercent-io/skills-template/supercent-io-skills-template-google-workspace

Google Workspace

Comprehensive AI agent skill for all Google Workspace document operations — Docs, Sheets, Slides, Drive, Gmail, Calendar, Chat, Forms, Admin SDK, and Apps Script — via official REST APIs.

When to use this skill

  • Creating or editing Google Docs, Sheets, Slides

  • Uploading, downloading, organizing Google Drive files and folders

  • Sending/reading Gmail, managing labels and drafts

  • Creating calendar events, inviting attendees, checking availability

  • Posting Google Chat messages, managing spaces

  • Building and reading Google Forms/surveys

  • Provisioning/managing Google Workspace users (Admin SDK)

  • Running automated workflows via Apps Script

Quick Setup

Step 1: Enable APIs in Google Cloud Console

Install gcloud CLI (if not available)

brew install --cask google-cloud-sdk # macOS

Or: curl https://sdk.cloud.google.com | bash

Enable all Workspace APIs

gcloud services enable docs.googleapis.com
sheets.googleapis.com slides.googleapis.com
drive.googleapis.com gmail.googleapis.com
calendar-json.googleapis.com chat.googleapis.com
forms.googleapis.com admin.googleapis.com
script.googleapis.com

Step 2: Install Python client library

pip install --upgrade
google-api-python-client
google-auth-httplib2
google-auth-oauthlib

Step 3: Authenticate

OAuth2 — interactive user auth (for accessing user's own data)

bash scripts/auth-setup.sh --oauth2 credentials.json

Service Account — server-to-server (for automation/backend)

bash scripts/auth-setup.sh --service-account service-account-key.json

API Reference by Product

Google Docs

Endpoint: https://docs.googleapis.com/v1

Scope: https://www.googleapis.com/auth/documents

from googleapiclient.discovery import build

docs = build('docs', 'v1', credentials=creds)

Create document

doc = docs.documents().create(body={'title': 'My Document'}).execute() doc_id = doc['documentId']

Read document

doc = docs.documents().get(documentId=doc_id).execute() content = doc.get('body', {}).get('content', [])

Edit: replace all text matching a pattern

requests = [{ 'replaceAllText': { 'containsText': {'text': '{{name}}', 'matchCase': False}, 'replaceText': 'Alice' } }] docs.documents().batchUpdate(documentId=doc_id, body={'requests': requests}).execute()

Insert text at position

requests = [{'insertText': {'location': {'index': 1}, 'text': 'Hello!\n'}}] docs.documents().batchUpdate(documentId=doc_id, body={'requests': requests}).execute()

Key batchUpdate operations: insertText , deleteContentRange , replaceAllText , updateTextStyle , updateParagraphStyle , insertTable , insertInlineImage , createHeader , createFooter , createNamedRange

Google Sheets

Endpoint: https://sheets.googleapis.com/v4

Scope: https://www.googleapis.com/auth/spreadsheets

sheets = build('sheets', 'v4', credentials=creds) ss = sheets.spreadsheets()

Create spreadsheet

spreadsheet = ss.create(body={ 'properties': {'title': 'My Sheet'}, 'sheets': [{'properties': {'title': 'Data'}}] }).execute() sheet_id = spreadsheet['spreadsheetId']

Write data

ss.values().update( spreadsheetId=sheet_id, range='Sheet1!A1', valueInputOption='USER_ENTERED', body={'values': [['Name', 'Score'], ['Alice', 95], ['Bob', 87]]} ).execute()

Read data

result = ss.values().get(spreadsheetId=sheet_id, range='Sheet1!A:B').execute() rows = result.get('values', [])

Append rows

ss.values().append( spreadsheetId=sheet_id, range='Sheet1!A1', valueInputOption='USER_ENTERED', body={'values': [['Charlie', 91]]} ).execute()

Batch update (format: freeze row 1, bold header)

ss.batchUpdate(spreadsheetId=sheet_id, body={'requests': [ {'updateSheetProperties': { 'properties': {'sheetId': 0, 'gridProperties': {'frozenRowCount': 1}}, 'fields': 'gridProperties.frozenRowCount' }}, {'repeatCell': { 'range': {'sheetId': 0, 'startRowIndex': 0, 'endRowIndex': 1}, 'cell': {'userEnteredFormat': {'textFormat': {'bold': True}}}, 'fields': 'userEnteredFormat.textFormat.bold' }} ]}).execute()

Google Slides

Endpoint: https://slides.googleapis.com/v1

Scope: https://www.googleapis.com/auth/presentations

slides = build('slides', 'v1', credentials=creds)

Create presentation

presentation = slides.presentations().create( body={'title': 'My Presentation'} ).execute() pres_id = presentation['presentationId']

Read presentation

pres = slides.presentations().get(presentationId=pres_id).execute() slide_ids = [s['objectId'] for s in pres.get('slides', [])]

Add a new slide

slides.presentations().batchUpdate(presentationId=pres_id, body={'requests': [ {'createSlide': { 'insertionIndex': 1, 'slideLayoutReference': {'predefinedLayout': 'TITLE_AND_BODY'} }} ]}).execute()

Replace placeholder text

slides.presentations().batchUpdate(presentationId=pres_id, body={'requests': [ {'replaceAllText': { 'containsText': {'text': '{{title}}', 'matchCase': False}, 'replaceText': 'Q1 Report' }} ]}).execute()

Get slide thumbnail

page_id = slide_ids[0] thumb = slides.presentations().pages().getThumbnail( presentationId=pres_id, pageObjectId=page_id, thumbnailProperties_thumbnailSize='LARGE' ).execute() image_url = thumb['contentUrl']

Google Drive

Endpoint: https://www.googleapis.com/drive/v3

Scope: https://www.googleapis.com/auth/drive

drive = build('drive', 'v3', credentials=creds)

Create folder

folder = drive.files().create(body={ 'name': 'My Folder', 'mimeType': 'application/vnd.google-apps.folder' }).execute() folder_id = folder['id']

Upload file

from googleapiclient.http import MediaFileUpload media = MediaFileUpload('report.pdf', mimetype='application/pdf') file = drive.files().create( body={'name': 'report.pdf', 'parents': [folder_id]}, media_body=media, fields='id' ).execute()

Search files

results = drive.files().list( q="name contains 'report' and mimeType='application/pdf'", fields='files(id, name, modifiedTime)' ).execute()

Share file

drive.permissions().create( fileId=file['id'], body={'type': 'user', 'role': 'reader', 'emailAddress': 'alice@example.com'}, sendNotificationEmail=True ).execute()

Export Google Doc to PDF

import io from googleapiclient.http import MediaIoBaseDownload request = drive.files().export_media(fileId=doc_id, mimeType='application/pdf') fh = io.BytesIO() downloader = MediaIoBaseDownload(fh, request) done = False while not done: _, done = downloader.next_chunk() with open('document.pdf', 'wb') as f: f.write(fh.getvalue())

Move file

drive.files().update( fileId=file['id'], addParents=folder_id, removeParents='root', fields='id, parents' ).execute()

Copy file (e.g., from template)

copy = drive.files().copy( fileId='TEMPLATE_FILE_ID', body={'name': 'New Document from Template', 'parents': [folder_id]} ).execute()

Gmail

Endpoint: https://gmail.googleapis.com/gmail/v1

Scope: https://www.googleapis.com/auth/gmail.modify

import base64 from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart

gmail = build('gmail', 'v1', credentials=creds)

Send email

def send_email(to, subject, body): msg = MIMEText(body) msg['to'] = to msg['subject'] = subject raw = base64.urlsafe_b64encode(msg.as_bytes()).decode() gmail.users().messages().send(userId='me', body={'raw': raw}).execute()

send_email('alice@example.com', 'Hello', 'This is the body.')

Send with attachment

msg = MIMEMultipart() msg['to'] = 'alice@example.com' msg['subject'] = 'Report' msg.attach(MIMEText('Please find the report attached.')) with open('report.pdf', 'rb') as f: from email.mime.application import MIMEApplication part = MIMEApplication(f.read(), Name='report.pdf') part['Content-Disposition'] = 'attachment; filename="report.pdf"' msg.attach(part) raw = base64.urlsafe_b64encode(msg.as_bytes()).decode() gmail.users().messages().send(userId='me', body={'raw': raw}).execute()

Search emails

results = gmail.users().messages().list( userId='me', q='from:boss@company.com subject:urgent is:unread' ).execute()

Read email

msg_id = results['messages'][0]['id'] msg = gmail.users().messages().get(userId='me', id=msg_id, format='full').execute() subject = next(h['value'] for h in msg['payload']['headers'] if h['name'] == 'Subject')

Create label and apply

label = gmail.users().labels().create( userId='me', body={'name': 'AI-Processed'} ).execute() gmail.users().messages().modify( userId='me', id=msg_id, body={'addLabelIds': [label['id']], 'removeLabelIds': ['UNREAD']} ).execute()

Create draft

raw_draft = base64.urlsafe_b64encode(MIMEText('Draft body').as_bytes()).decode() gmail.users().drafts().create( userId='me', body={'message': {'raw': raw_draft}} ).execute()

Set vacation responder

gmail.users().settings().updateVacation( userId='me', body={ 'enableAutoReply': True, 'responseSubject': 'Out of Office', 'responseBodyPlainText': 'I am OOO until Monday.', 'startTime': '1704067200000', # Unix ms 'endTime': '1704326400000' } ).execute()

Google Calendar

Endpoint: https://www.googleapis.com/calendar/v3

Scope: https://www.googleapis.com/auth/calendar

from datetime import datetime, timedelta import pytz

calendar = build('calendar', 'v3', credentials=creds)

Create event

event = calendar.events().insert( calendarId='primary', body={ 'summary': 'Team Standup', 'description': 'Daily sync', 'start': {'dateTime': '2026-03-15T09:00:00+09:00', 'timeZone': 'Asia/Seoul'}, 'end': {'dateTime': '2026-03-15T09:30:00+09:00', 'timeZone': 'Asia/Seoul'}, 'attendees': [ {'email': 'alice@example.com'}, {'email': 'bob@example.com'}, ], 'conferenceData': { 'createRequest': {'requestId': 'meeting-001', 'conferenceSolutionKey': {'type': 'hangoutsMeet'}} }, 'recurrence': ['RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR'] }, conferenceDataVersion=1 ).execute() meet_link = event.get('hangoutLink')

List today's events

now = datetime.utcnow().isoformat() + 'Z' end_of_day = (datetime.utcnow() + timedelta(hours=24)).isoformat() + 'Z' events_result = calendar.events().list( calendarId='primary', timeMin=now, timeMax=end_of_day, singleEvents=True, orderBy='startTime' ).execute() events = events_result.get('items', [])

Check free/busy

body = { 'timeMin': now, 'timeMax': end_of_day, 'items': [{'id': 'alice@example.com'}, {'id': 'bob@example.com'}] } freebusy = calendar.freebusy().query(body=body).execute()

Block time / set OOO

calendar.events().insert( calendarId='primary', body={ 'summary': 'Out of Office', 'eventType': 'outOfOffice', 'start': {'date': '2026-03-20'}, 'end': {'date': '2026-03-22'} } ).execute()

Share calendar

calendar.acl().insert( calendarId='primary', body={'role': 'reader', 'scope': {'type': 'user', 'value': 'alice@example.com'}} ).execute()

Google Chat

Endpoint: https://chat.googleapis.com/v1

Scope: https://www.googleapis.com/auth/chat.messages

chat = build('chat', 'v1', credentials=creds)

Send message to a space

space_name = 'spaces/SPACE_ID' # From Chat URL chat.spaces().messages().create( parent=space_name, body={ 'text': 'Hello from AI agent! 🤖', 'cards_v2': [{ 'cardId': 'card1', 'card': { 'header': {'title': 'Update', 'subtitle': 'Automated report'}, 'sections': [{'widgets': [{'textParagraph': {'text': 'Task completed.'}}]}] } }] } ).execute()

Create a new space

space = chat.spaces().create( body={ 'spaceType': 'SPACE', 'displayName': 'Project Alpha' } ).execute()

List spaces

spaces = chat.spaces().list().execute()

Add member to space

chat.spaces().members().create( parent=space['name'], body={'member': {'name': 'users/alice@example.com', 'type': 'HUMAN'}} ).execute()

Find or create direct message

dm = chat.spaces().findDirectMessage(name='users/alice@example.com').execute()

Google Forms

Endpoint: https://forms.googleapis.com/v1

Scope: https://www.googleapis.com/auth/forms.body

forms = build('forms', 'v1', credentials=creds)

Create form

form = forms.forms().create(body={ 'info': {'title': 'Customer Feedback Survey', 'documentTitle': 'Customer Feedback'} }).execute() form_id = form['formId']

Add questions

forms.forms().batchUpdate(formId=form_id, body={'requests': [ { 'createItem': { 'item': { 'title': 'How satisfied are you?', 'questionItem': { 'question': { 'required': True, 'scaleQuestion': { 'low': 1, 'high': 5, 'lowLabel': 'Not satisfied', 'highLabel': 'Very satisfied' } } } }, 'location': {'index': 0} } }, { 'createItem': { 'item': { 'title': 'Any comments?', 'questionItem': { 'question': { 'required': False, 'textQuestion': {'paragraph': True} } } }, 'location': {'index': 1} } } ]}).execute()

Get form responses

responses = forms.forms().responses().list(formId=form_id).execute() for r in responses.get('responses', []): for qid, ans in r.get('answers', {}).items(): print(qid, ans.get('textAnswers', {}).get('answers', []))

Admin SDK — Directory API

Endpoint: https://admin.googleapis.com

Scope: https://www.googleapis.com/auth/admin.directory.user

Requires: Service account with domain-wide delegation

from google.oauth2 import service_account

SA_FILE = 'service-account.json' SCOPES = ['https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/admin.directory.group'] creds = service_account.Credentials.from_service_account_file( SA_FILE, scopes=SCOPES ).with_subject('admin@yourdomain.com')

admin = build('admin', 'directory_v1', credentials=creds)

Create user

admin.users().insert(body={ 'primaryEmail': 'newuser@yourdomain.com', 'name': {'givenName': 'New', 'familyName': 'User'}, 'password': 'TemporaryPassword123!', 'changePasswordAtNextLogin': True }).execute()

List users

users_result = admin.users().list(domain='yourdomain.com', maxResults=100).execute() for user in users_result.get('users', []): print(user['primaryEmail'], user.get('suspended', False))

Suspend user

admin.users().update( userKey='user@yourdomain.com', body={'suspended': True} ).execute()

Add user to group

admin.members().insert( groupKey='team@yourdomain.com', body={'email': 'user@yourdomain.com', 'role': 'MEMBER'} ).execute()

List groups

groups = admin.groups().list(domain='yourdomain.com').execute()

Apps Script API

Endpoint: https://script.googleapis.com/v1

Scope: https://www.googleapis.com/auth/script.projects

script = build('script', 'v1', credentials=creds)

Run a deployed function

response = script.scripts().run( scriptId='DEPLOYED_SCRIPT_ID', body={ 'function': 'myFunction', 'parameters': ['arg1', 42] } ).execute() result = response.get('response', {}).get('result')

Common Automation Patterns

Pattern 1: Create Document from Template

def create_doc_from_template(drive, docs, template_id, replacements, dest_folder_id=None): """Clone a template Google Doc and fill in placeholders.""" body = {'name': replacements.get('{{title}}', 'New Document')} if dest_folder_id: body['parents'] = [dest_folder_id] copy = drive.files().copy(fileId=template_id, body=body).execute() new_id = copy['id'] requests = [ {'replaceAllText': {'containsText': {'text': k, 'matchCase': False}, 'replaceText': v}} for k, v in replacements.items() ] if requests: docs.documents().batchUpdate(documentId=new_id, body={'requests': requests}).execute() return new_id

Pattern 2: Bulk Append to Spreadsheet

def bulk_append_rows(sheets, spreadsheet_id, sheet_name, rows): """Append multiple rows to a sheet in one API call.""" sheets.spreadsheets().values().append( spreadsheetId=spreadsheet_id, range=f'{sheet_name}!A1', valueInputOption='USER_ENTERED', insertDataOption='INSERT_ROWS', body={'values': rows} ).execute()

Pattern 3: Create Meeting Notes Document

def create_meeting_notes(calendar, drive, docs, event_id): """Create a Google Doc for meeting notes and share with attendees.""" event = calendar.events().get(calendarId='primary', eventId=event_id).execute() attendees = [a['email'] for a in event.get('attendees', [])] title = f"Meeting Notes: {event['summary']} — {event['start'].get('dateTime', event['start'].get('date'))}" doc = docs.documents().create(body={'title': title}).execute() doc_id = doc['documentId'] for email in attendees: drive.permissions().create( fileId=doc_id, body={'type': 'user', 'role': 'writer', 'emailAddress': email}, sendNotificationEmail=True ).execute() return doc_id

Pattern 4: Form Response to Sheet

def sync_form_to_sheet(forms, sheets, form_id, spreadsheet_id): """Sync all form responses to a Google Sheet.""" responses = forms.forms().responses().list(formId=form_id).execute() form_data = forms.forms().get(formId=form_id).execute() questions = { item['itemId']: item.get('title', '') for item in form_data.get('items', []) if 'questionItem' in item } headers = ['Timestamp'] + list(questions.values()) rows = [headers] for resp in responses.get('responses', []): row = [resp.get('createTime', '')] for qid in questions: ans = resp.get('answers', {}).get(qid, {}) text_ans = ans.get('textAnswers', {}).get('answers', [{}]) row.append(text_ans[0].get('value', '') if text_ans else '') rows.append(row) sheets.spreadsheets().values().update( spreadsheetId=spreadsheet_id, range='Sheet1!A1', valueInputOption='USER_ENTERED', body={'values': rows} ).execute()

Rate Limits & Best Practices

API Quota Retry Strategy

Docs API 300 req/min/user Exponential backoff on 429

Sheets API 300 req/min Batch operations reduce quota usage

Drive API 1,000 req/100 sec Use fields param to reduce payload

Gmail API 250 quota units/user/sec batchModify for bulk operations

Calendar API 1,000,000 req/day Use timeMin /timeMax to limit list results

Admin SDK 10 user creates/domain/sec Add time.sleep(0.15) between creates

import time from googleapiclient.errors import HttpError

def api_call_with_retry(func, *args, max_retries=5, **kwargs): """Wrapper that retries on 429/503 with exponential backoff.""" for attempt in range(max_retries): try: return func(*args, **kwargs).execute() except HttpError as e: if e.resp.status in (429, 503) and attempt < max_retries - 1: wait = (2 ** attempt) + 0.1 print(f"Rate limit hit, waiting {wait:.1f}s...") time.sleep(wait) else: raise

Scopes Reference

Product Read Scope Write Scope

Docs auth/documents.readonly

auth/documents

Sheets auth/spreadsheets.readonly

auth/spreadsheets

Slides auth/presentations.readonly

auth/presentations

Drive auth/drive.readonly

auth/drive

Gmail auth/gmail.readonly

auth/gmail.modify

Calendar auth/calendar.readonly

auth/calendar

Chat auth/chat.messages.readonly

auth/chat.messages

Forms auth/forms.body.readonly

auth/forms.body

Admin SDK auth/admin.directory.user.readonly

auth/admin.directory.user

References

  • Google Workspace Developer Hub

  • Google Docs API Reference

  • Google Sheets API Reference

  • Google Slides API Reference

  • Google Drive API v3 Reference

  • Gmail API Reference

  • Google Calendar API Reference

  • Google Chat API Reference

  • Google Forms API Reference

  • Admin SDK Directory API Reference

  • Apps Script REST API

  • Auth Overview & Credentials Setup

  • Enable APIs Guide

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

workflow-automation

When to use this skill

Repository Source
Automation

deployment-automation

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

git-workflow

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

copilot-coding-agent

No summary provided by upstream source.

Repository SourceNeeds Review