The real problem
Why this is harder than it looks
Most teams look at the Google Docs API and figure the integration is a weekend project. The read and write primitives are clean. The documentation is thorough. The problems start once you try to ship this for real users.
First, there's the OAuth app verification process. Google requires any application that requests Docs API scopes — https://www.googleapis.com/auth/documents — to go through a security review before it can be used by accounts outside your development workspace. Until that review clears, your OAuth consent screen displays an "unverified app" warning that users have to click through manually. For agents interacting with documents, that warning kills trust on the spot. The review takes time, requires a privacy policy and a working demo environment, and blocks production launch for an unpredictable window.
Then there's the scope selection problem. The Docs API and Drive API overlap — you can read and write documents through either, with meaningfully different consent screen footprints. Choosing documents scope without also understanding when you need drive.file or drive.readonly leads to either over-requesting permissions (flagged in review, rejected by cautious users) or under-requesting them (runtime 403s that look like auth bugs but are actually scope gaps). And if your agent's capabilities expand later, adding scopes requires a fresh OAuth authorization from every user who already connected — there's no way to silently widen the token.
In production, each user connects their own Google account. That means per-user token isolation, proactive refresh before expiry, and graceful handling when a user revokes your app from their Google account settings. Revocation doesn't generate a webhook — you discover it on the next API call, as a 401 with no helpful context. Handling all of this correctly, for an organization full of document-writing users, is a month of plumbing that has nothing to do with what your agent actually does.
Scalekit handles the token lifecycle, manages credentials per user, and detects revocation before it causes silent failures. Your agent names a tool and passes a document ID. Everything else is handled.
Capabilities
What your agent can do with Google Docs
Once connected, your agent has 3 pre-built tools for the full Google Docs document lifecycle:
- Create new documents: spin up a blank Google Doc with an optional title and get back the document ID for subsequent operations
- Read document content and structure: retrieve the full document tree — text content, paragraph formatting, tables, inline objects, named styles, and document metadata
- Update documents with batch operations: insert text, apply paragraph and text formatting, manage tables, and manipulate structure using Google's batchUpdate API, with optional write-control for revision safety
Setup context
What we're building
This guide connects a writing assistant agent to Google Docs — helping team members create, read, and update documents without leaving your product.
🤖
Example agent
Writing assistant creating and editing Google Docs on behalf of each team member
🔐
Auth model
B2B SaaS — each user connects their own Google account. identifier = your user ID
Setup
1 Setup: One SDK, One credential
Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Google secrets, no user tokens, nothing belonging to your customers.
pip install scalekit-sdk-python
import scalekit.client
import os
from dotenv import load_dotenv
load_dotenv()
scalekit = scalekit.client.ScalekitClient(
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
env_url=os.getenv("SCALEKIT_ENV_URL"),
)
actions = scalekit.actions
Connected Accounts
2 Per-User Auth: Creating connected accounts
Each user gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.
response = actions.get_or_create_connected_account(
connection_name="google_docs",
identifier="user_gdoc_789" # your internal user ID
)
connected_account = response.connected_account
print(f"Status: {connected_account.status}")
# Status: PENDING — user hasn't authorized yet
This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.
Authorization Flow
3 The authorization flow
The user authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.
if connected_account.status != "ACTIVE":
link = actions.get_authorization_link(
connection_name="google_docs",
identifier="user_gdoc_789"
)
# Redirect user → Google consent screen
# Scalekit captures the token on callback
return redirect(link.link)
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access
tokens refresh before expiry. If a user revokes access from their Google account settings, the account moves to REVOKED
— no silent 401s on the next call. Check account.status before critical write operations.
Bring Your Own Credentials — required for Google Docs
Google Docs requires your own OAuth app to pass app verification with your branding on the consent screen.
Register a Google Cloud project, enable the Docs API, create an OAuth client ID, and paste the credentials
into the Scalekit dashboard. Token management stays fully handled.
Already have credentials?
Calling Google Docs
4 Calling Google Docs: What your agent writes
With the connected account active, your agent calls tools using actions.execute_tool(). Name the tool, pass parameters. Scalekit handles token retrieval, request construction, and response parsing.
Create a new document
Spin up a blank Google Doc with a title. The response includes the new document's ID — pass it to subsequent read or update calls.
result = actions.execute_tool(
identifier="user_gdoc_789",
tool_name="googledocs_create_document",
tool_input={
"title": "Q3 Product Strategy — Draft"
}
)
# Returns: { "documentId": "1BxiMVs...", "title": "Q3 Product Strategy — Draft", ... }
doc_id = result["documentId"]
Read a document
Retrieve the full content and structure of any Google Doc the user has access to. The response includes the complete document tree — paragraphs, tables, inline objects, named styles, and revision metadata. Use suggestions_view_mode to control how tracked changes appear.
result = actions.execute_tool(
identifier="user_gdoc_789",
tool_name="googledocs_read_document",
tool_input={
"document_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"suggestions_view_mode": "PREVIEW_WITHOUT_SUGGESTIONS"
}
)
# Returns full document body: paragraphs, tables, named styles, revision ID
Insert text into a document
Update a document using Google's batchUpdate request format. Index 1 places content at the very beginning of the document body — index 0 is not a valid insertion point.
result = actions.execute_tool(
identifier="user_gdoc_789",
tool_name="googledocs_update_document",
tool_input={
"document_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"requests": [
{
"insertText": {
"location": { "index": 1 },
"text": "Executive Summary\n\nThis document outlines the Q3 product strategy..."
}
}
]
}
)
Apply paragraph formatting
Batch a style update alongside an insert. All requests in a single batchUpdate call are applied atomically — if any request is invalid, none are applied.
result = actions.execute_tool(
identifier="user_gdoc_789",
tool_name="googledocs_update_document",
tool_input={
"document_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"requests": [
{
"insertText": {
"location": { "index": 1 },
"text": "Executive Summary\n"
}
},
{
"updateParagraphStyle": {
"range": { "startIndex": 1, "endIndex": 19 },
"paragraphStyle": { "namedStyleType": "HEADING_1" },
"fields": "namedStyleType"
}
}
],
"write_control": {
"requiredRevisionId": "AbCdEf123" # optional: guard against concurrent edits
}
}
)
Framework wiring
5 Wiring into your agent framework
Scalekit integrates directly with LangChain. The agent decides what to create or update; Scalekit handles auth on every invocation. No token plumbing in your agent logic.
from langchain_anthropic import ChatAnthropic
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from scalekit.langchain import get_tools
gdoc_tools = get_tools(
connection_name="google_docs",
identifier="user_gdoc_789"
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a writing assistant. Use the available tools to create and edit Google Docs on the user's behalf."),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), gdoc_tools, prompt)
result = AgentExecutor(agent=agent, tools=gdoc_tools).invoke({
"input": "Create a new doc called 'Meeting Notes — July 10' and add a summary of the key decisions we discussed"
})
Other frameworks supported
Tool reference
Google Docs tools
3 pre-built tools covering the full document lifecycle. Your agent calls tools by name — no API wrappers to write.
googledocs_create_document
Create a new blank Google Doc with an optional title. Returns the document ID and metadata needed for subsequent read or update calls.
Read the complete content and structure of a Google Doc — paragraphs, text runs, tables, inline objects, named styles, and revision metadata. Accepts an optional suggestions_view_mode to control how tracked changes are rendered.
googledocs_update_document
Update an existing document using Google's batchUpdate request array. Supports inserting and deleting text, applying paragraph and text styles, creating and modifying tables, and inserting page breaks. Accepts an optional write_control object with requiredRevisionId to guard against conflicting concurrent edits.
Connector notes
Google Docs-specific behavior
App verification is a prerequisite — not an afterthought
Any public application requesting Google Docs API scopes must complete Google's OAuth app verification
before users outside your organization will see a clean consent screen. Until verification clears, users
see an "unverified app" warning and must click through an extra confirmation. The process requires a
privacy policy, homepage, and a working demo environment. Plan verification lead time into your launch
schedule.
Adding scopes later forces re-authorization for all users
Google does not allow silently widening an existing token's scope. If your agent later needs access to an
additional scope — for example, adding drive.file to read document metadata — every user who already
authorized must go through the OAuth flow again. Design your scope set to cover your full roadmap before
deploying to production users.
batchUpdate requests are atomic — all or nothing
Google Docs applies all requests in a single batchUpdate call atomically. If any request in the array is
invalid, none are applied. Validate your request structure carefully before sending large batches. Within a
single call, later requests must account for index shifts caused by earlier insertions or deletions.
Infrastructure decision
Why not build this yourself
The Google Docs OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for:
PROBLEM 01
Google's OAuth app verification process blocks production launch until review completes — no clear timeline, requires privacy policy and a working demo environment
PROBLEM 02
Scope selection is a one-shot decision — adding scopes later requires full re-authorization from every connected user, with no way to silently widen an existing token
PROBLEM 03
Per-user token isolation across a multi-tenant system — each user's Google credentials must be completely invisible to others, even on the same team
PROBLEM 04
Revocation detection — Google doesn't send a webhook when a user disconnects your app; you discover it on the next API call and need a graceful recovery path
That's one connector. Your agent product will eventually need Salesforce, Gmail, Slack, Notion, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.
Scalekit maintains every connector. You maintain none of them.