Operate all Google Workspace services through the gws CLI from OpenClaw.
gws CLI installed: npm install -g @googleworkspace/cliWhen a user wants to start using this skill or if credentials are missing, you (the AI agent) MUST follow this specific authentication workflow:
Ask for client_secret.json: Prompt the user to provide their Google Cloud OAuth client_secret.json. They can either upload the file or paste its contents into the chat. Once they do, save it as credentials.json in the workspace.
Explain the Flow: Once you have the file, explain to the user exactly how the auth will work:
"I have saved your credentials. I am now going to start the authentication process. I'll provide you with a Google login link. You'll need to click it, authorize your account, and then your browser will redirect to a blank
http://localhost...page. Copy that full localhost URL and paste it back to me here!"
Run Authentication: Run gws auth login in the background (using your exec tool). Extract the generated Google OAuth URL from the output and send it to the user.
Complete Handshake: When the user replies with the http://localhost... callback URL, use curl to fetch that exact URL. This completes the local webserver handshake for gws. Confirm successful authentication with the user.
bash
gws auth setup # creates GCP project, enables APIs, logs in
gws auth login -s drive,gmail,sheets,calendar # pick services you need
Complete auth on a machine with a browser, then export:
bash
gws auth export --unmasked > credentials.json
Security notes:
- Set file permissions:
chmod 600 credentials.json- Do not commit credential files to git — add
credentials.jsonto your.gitignore- For production environments, prefer using a service account instead of user credentials
On the headless server:
bash
export GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/credentials.json
bash
export GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/service-account.json
bash
export GOOGLE_WORKSPACE_CLI_TOKEN=$(gcloud auth print-access-token)
Priority: Token env > Credentials file env > gws auth login store > plaintext file.
bash
gws <service> <resource> <method> [--params '{}'] [--json '{}'] [flags]
All responses are structured JSON. Use jq for extraction.
FlagPurpose--dry-runPreview request without executing--page-allStream all pages as NDJSON--fields 'a,b'Select response fields--output tableTable output for humans
bash
gws --help # list all services
gws drive --help # list resources in a service
gws drive files --help # list methods on a resource
gws schema drive.files.list # full request/response schema
bash
# List recent files
gws drive files list --params '{"pageSize": 10}'
# Search files
gws drive files list --params '{"q": "name contains '\''report'\''", "pageSize": 20}'
# Upload a file
gws drive +upload ./report.pdf
# Download a file
gws drive files get --params '{"fileId": "FILE_ID", "alt": "media"}' > output.pdf
# Create a folder
gws drive files create --json '{"name": "Project", "mimeType": "application/vnd.google-apps.folder"}'
# Share a file
gws drive permissions create \
--params '{"fileId": "FILE_ID"}' \
--json '{"role": "reader", "type": "user", "emailAddress": "user@example.com"}'
# List all pages
gws drive files list --params '{"pageSize": 100}' --page-all | jq -r '.files[].name'
bash
# List inbox messages
gws gmail users-messages list --params '{"userId": "me", "maxResults": 10}'
# Read a message
gws gmail users-messages get --params '{"userId": "me", "id": "MSG_ID"}'
# Send an email
gws gmail users-messages send \
--params '{"userId": "me"}' \
--json '{"raw": "BASE64_ENCODED_EMAIL"}'
# Search messages
gws gmail users-messages list --params '{"userId": "me", "q": "from:boss@company.com is:unread"}'
# List labels
gws gmail users-labels list --params '{"userId": "me"}'
# Create a filter
gws gmail users-settings-filters create \
--params '{"userId": "me"}' \
--json '{"criteria": {"from": "noreply@example.com"}, "action": {"addLabelIds": ["LABEL_ID"], "removeLabelIds": ["INBOX"]}}'
bash
# List upcoming events
gws calendar events list --params '{"calendarId": "primary", "timeMin": "2026-01-01T00:00:00Z", "maxResults": 10, "orderBy": "startTime", "singleEvents": true}'
# Create an event
gws calendar events insert \
--params '{"calendarId": "primary"}' \
--json '{"summary": "Team Sync", "start": {"dateTime": "2026-03-07T10:00:00+08:00"}, "end": {"dateTime": "2026-03-07T11:00:00+08:00"}, "attendees": [{"email": "user@example.com"}]}'
# Delete an event
gws calendar events delete --params '{"calendarId": "primary", "eventId": "EVENT_ID"}'
# Find free/busy slots
gws calendar freebusy query \
--json '{"timeMin": "2026-03-07T00:00:00Z", "timeMax": "2026-03-07T23:59:59Z", "items": [{"id": "user@example.com"}]}'
bash
# Create a spreadsheet
gws sheets spreadsheets create --json '{"properties": {"title": "Q1 Budget"}}'
# Read cell values
gws sheets spreadsheets-values get --params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1:D10"}'
# Write values
gws sheets spreadsheets-values update \
--params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1", "valueInputOption": "USER_ENTERED"}' \
--json '{"values": [["Name", "Amount"], ["Rent", "2000"]]}'
# Append a row
gws sheets spreadsheets-values append \
--params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1", "valueInputOption": "USER_ENTERED"}' \
--json '{"values": [["New Item", "500"]]}'
bash
# Create a document
gws docs documents create --json '{"title": "Meeting Notes"}'
# Get document content
gws docs documents get --params '{"documentId": "DOC_ID"}'
# Insert text (batchUpdate)
gws docs documents batchUpdate \
--params '{"documentId": "DOC_ID"}' \
--json '{"requests": [{"insertText": {"location": {"index": 1}, "text": "Hello World\n"}}]}'
bash
# List spaces
gws chat spaces list
# Send a message
gws chat spaces messages create \
--params '{"parent": "spaces/SPACE_ID"}' \
--json '{"text": "Deploy complete ✅"}'
bash
# List task lists
gws tasks tasklists list
# List tasks
gws tasks tasks list --params '{"tasklist": "TASKLIST_ID"}'
# Create a task
gws tasks tasks insert \
--params '{"tasklist": "TASKLIST_ID"}' \
--json '{"title": "Review PR", "due": "2026-03-10T00:00:00Z"}'
bash
# List users
gws admin users list --params '{"domain": "example.com"}'
# Get user details
gws admin users get --params '{"userKey": "user@example.com"}'
bash
# Find unread emails from boss, extract subjects
gws gmail users-messages list --params '{"userId": "me", "q": "from:boss is:unread"}' \
| jq -r '.messages[].id' \
| while read id; do
gws gmail users-messages get --params "{\"userId\": \"me\", \"id\": \"$id\"}" \
| jq -r '.payload.headers[] | select(.name=="Subject") | .value'
done
Always use --dry-run before destructive operations:
bash
gws drive files delete --params '{"fileId": "FILE_ID"}' --dry-run
gws schema <method> to discover exact parameter names and types.--params for URL/query parameters and --json for request body.jq for field extraction in agent pipelines.--page-all for full result sets with automatic pagination.For 50+ ready-made workflow recipes (label & archive emails, organize Drive folders, schedule meetings, etc.), see the official recipe library.
The gws CLI is not an officially supported Google product. It is a community/experimental tool. Use at your own discretion, and refer to the upstream repository for license and support details.
IssueFixgws: command not found``npm install -g @googleworkspace/cliAuth fails / scope errorgws auth login -s drive,gmail (pick specific services)"Access blocked" on loginAdd yourself as test user in GCP OAuth consent screenHeadless serverExport creds from a desktop, set GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILERate limitedAdd delays between calls, reduce pageSize