google-docs-skillIntegrate with Google Docs API to create, read, update, format, and manage documents using OAuth 2.0 authentication.
Install via ClawdBot CLI:
clawdbot install zagran/google-docs-skillDirect access to the Google Docs API using OAuth 2.0. Create documents, insert and format text, and manage document content.
export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export GOOGLE_REFRESH_TOKEN="your-refresh-token"
Use the following Python script to obtain your refresh token (one-time setup):
import urllib.request
import urllib.parse
import json
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
import os
# OAuth configuration
CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID')
CLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET')
REDIRECT_URI = 'http://localhost:8080'
SCOPES = 'https://www.googleapis.com/auth/documents'
# Step 1: Get authorization code
auth_url = (
f"https://accounts.google.com/o/oauth2/v2/auth?"
f"client_id={CLIENT_ID}&"
f"redirect_uri={REDIRECT_URI}&"
f"response_type=code&"
f"scope={urllib.parse.quote(SCOPES)}&"
f"access_type=offline&"
f"prompt=consent"
)
print(f"Opening browser for authorization...")
webbrowser.open(auth_url)
# Step 2: Capture authorization code
auth_code = None
class OAuthHandler(BaseHTTPRequestHandler):
def do_GET(self):
global auth_code
query = urllib.parse.urlparse(self.path).query
params = urllib.parse.parse_qs(query)
auth_code = params.get('code', [None])[0]
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'<html><body><h1>Authorization successful!</h1><p>You can close this window.</p></body></html>')
server = HTTPServer(('localhost', 8080), OAuthHandler)
server.handle_request()
# Step 3: Exchange code for tokens
if auth_code:
data = urllib.parse.urlencode({
'code': auth_code,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'grant_type': 'authorization_code'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
print(f"\nRefresh Token: {response['refresh_token']}")
print(f"Access Token: {response['access_token']}")
print(f"\nSet your refresh token:")
print(f"export GOOGLE_REFRESH_TOKEN=\"{response['refresh_token']}\"")
Before making API calls, get a fresh access token:
import urllib.request
import urllib.parse
import json
import os
def get_access_token():
"""Get a fresh access token using refresh token"""
data = urllib.parse.urlencode({
'client_id': os.environ['GOOGLE_CLIENT_ID'],
'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],
'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],
'grant_type': 'refresh_token'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
return response['access_token']
# Store for reuse
access_token = get_access_token()
print(f"Access Token: {access_token}")
https://docs.googleapis.com/v1
All requests require the access token in the Authorization header:
Authorization: Bearer {access_token}
import urllib.request
import json
import os
# Get access token first (using function from above)
access_token = get_access_token()
# Get document
req = urllib.request.Request('https://docs.googleapis.com/v1/documents/{documentId}')
req.add_header('Authorization', f'Bearer {access_token}')
doc = json.load(urllib.request.urlopen(req))
print(json.dumps(doc, indent=2))
import urllib.request
import json
access_token = get_access_token()
data = json.dumps({'title': 'My New Document'}).encode()
req = urllib.request.Request(
'https://docs.googleapis.com/v1/documents',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
doc_id = response['documentId']
print(f"Created document: {doc_id}")
print(f"URL: https://docs.google.com/document/d/{doc_id}/edit")
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}'
)
req.add_header('Authorization', f'Bearer {access_token}')
doc = json.load(urllib.request.urlopen(req))
# Insert text at beginning
updates = {
'requests': [
{
'insertText': {
'location': {'index': 1},
'text': 'Hello, World!\n\n'
}
}
]
}
data = json.dumps(updates).encode()
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
{
'requests': [
{
'insertText': {
'location': {'index': 1},
'text': 'Your text here'
}
}
]
}
{
'requests': [
{
'updateTextStyle': {
'range': {
'startIndex': 1,
'endIndex': 10
},
'textStyle': {
'bold': True,
'italic': True,
'fontSize': {
'magnitude': 14,
'unit': 'PT'
}
},
'fields': 'bold,italic,fontSize'
}
}
]
}
{
'requests': [
{
'insertTable': {
'location': {'index': 1},
'rows': 3,
'columns': 4
}
}
]
}
{
'requests': [
{
'replaceAllText': {
'containsText': {
'text': '{{placeholder}}',
'matchCase': True
},
'replaceText': 'Actual value'
}
}
]
}
{
'requests': [
{
'deleteContentRange': {
'range': {
'startIndex': 1,
'endIndex': 50
}
}
}
]
}
{
'requests': [
{
'insertPageBreak': {
'location': {'index': 1}
}
}
]
}
{
'requests': [
{
'updateParagraphStyle': {
'range': {
'startIndex': 1,
'endIndex': 50
},
'paragraphStyle': {
'namedStyleType': 'HEADING_1',
'alignment': 'CENTER'
},
'fields': 'namedStyleType,alignment'
}
}
]
}
import urllib.request
import urllib.parse
import json
import os
def get_access_token():
"""Get fresh access token"""
data = urllib.parse.urlencode({
'client_id': os.environ['GOOGLE_CLIENT_ID'],
'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],
'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],
'grant_type': 'refresh_token'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
return response['access_token']
def create_document(title, access_token):
"""Create a new document"""
data = json.dumps({'title': title}).encode()
req = urllib.request.Request(
'https://docs.googleapis.com/v1/documents',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
return response['documentId']
def batch_update(doc_id, requests, access_token):
"""Apply batch updates to document"""
data = json.dumps({'requests': requests}).encode()
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
return json.load(urllib.request.urlopen(req))
# Main workflow
access_token = get_access_token()
# Create document
doc_id = create_document('Project Report', access_token)
print(f"Created: https://docs.google.com/document/d/{doc_id}/edit")
# Add content with formatting
requests = [
# Insert title
{
'insertText': {
'location': {'index': 1},
'text': 'Project Report\n\n'
}
},
# Format title as heading
{
'updateParagraphStyle': {
'range': {'startIndex': 1, 'endIndex': 15},
'paragraphStyle': {'namedStyleType': 'HEADING_1'},
'fields': 'namedStyleType'
}
},
# Add body text
{
'insertText': {
'location': {'index': 16},
'text': 'Executive Summary\n\nThis report covers...\n\n'
}
},
# Format section header
{
'updateParagraphStyle': {
'range': {'startIndex': 16, 'endIndex': 34},
'paragraphStyle': {'namedStyleType': 'HEADING_2'},
'fields': 'namedStyleType'
}
}
]
batch_update(doc_id, requests, access_token)
print("Document updated successfully!")
endOfSegmentLocation'fields': 'bold,fontSize,foregroundColor'| Status Code | Meaning |
|-------------|---------|
| 400 | Bad Request - Invalid request format |
| 401 | Unauthorized - Invalid or expired access token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Document doesn't exist |
| 429 | Rate Limited - Too many requests |
Access tokens expire after 1 hour. If you get a 401 error, refresh the token:
try:
# Make API call
req = urllib.request.Request(url)
req.add_header('Authorization', f'Bearer {access_token}')
response = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
if e.code == 401:
# Refresh token and retry
access_token = get_access_token()
req = urllib.request.Request(url)
req.add_header('Authorization', f'Bearer {access_token}')
response = urllib.request.urlopen(req)
endOfSegmentLocation for appendingGenerated Mar 1, 2026
Automatically create and populate Google Docs with structured data from databases or APIs, such as generating weekly sales reports or project status updates. This reduces manual data entry and ensures consistency in document formatting across teams.
Use the skill to draft, format, and update marketing content like blog posts, newsletters, and press releases directly from a content management system. It streamlines collaboration by allowing automated text insertion and styling based on predefined templates.
Teachers or e-learning platforms can generate customized lesson plans, worksheets, and study guides by inserting text and formatting dynamically. This saves time in preparing educational resources and allows for easy updates based on curriculum changes.
Law firms can automate the creation of standard legal documents such as contracts or agreements by populating templates with client-specific details. This ensures accuracy and compliance while reducing the risk of manual errors in critical documents.
Integrate the skill into project management software to enable real-time document updates and formatting changes based on team inputs. This enhances productivity by automating document synchronization and version control in collaborative environments.
Offer the skill as part of a subscription-based software service that automates document creation for businesses, charging monthly fees based on usage tiers. This model provides recurring revenue by solving document management inefficiencies for clients.
Provide basic document automation features for free to attract users, with premium upgrades for advanced formatting, batch processing, or API rate limits. This approach builds a user base and monetizes through upselling enhanced capabilities.
Offer consulting services to integrate the skill into existing enterprise systems, tailoring it to specific workflows and providing ongoing support. Revenue is generated through project-based fees and maintenance contracts for custom solutions.
💬 Integration Tip
Ensure OAuth tokens are securely stored and refreshed automatically to avoid authentication failures during API calls.
Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Gmail, Calendar, Drive, Docs, Sheets — NO Google Cloud Console required. Just OAuth sign-in. Zero setup complexity vs traditional Google API integrations.
Google Drive API integration with managed OAuth. List, search, create, and manage files and folders. Use this skill when users want to interact with Google D...
Google Sheets API integration with managed OAuth. Read and write spreadsheet data, create sheets, apply formatting, and manage ranges. Use this skill when users want to read from or write to Google Sheets. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).