
A rep types "get me the latest enterprise pricing sheet" into your assistant. The agent returns a file named Enterprise Pricing.pdf, the rep pastes the number into a customer proposal, and the number is from last quarter. Nobody edited a wrong field; the agent retrieved a file that looked current and was not. For a sales enablement agent on Google Drive, "find a file" is the easy part. Returning the one current, approved file that this specific rep is allowed to see is the part that breaks in production, and it breaks for reasons that live in your retrieval and auth architecture, not your prompt.
The request is one sentence. The correct answer satisfies three independent constraints, and a naive retrieval layer usually gets at most one of them right.
The rest of this build is these three gates, in order, wired into a LangChain agent. Nothing else earns a place in the pipeline.
The default architecture for "chat with your Drive" is a sync job that embeds Drive files into a vector store, then answers from similarity search. It is the wrong authority for a sales enablement agent, for two distinct reasons that are worth separating because they have different fixes.
The first failure is a freshness problem. The second is an authorization problem, and it is the one that turns a demo into an incident. Both dissolve when Drive itself is the authority: a live query against Drive returns Drive's current state, and a query issued through the rep's own grant returns only what the rep is allowed to see. Keep a vector index if you want semantic recall ("the deck that covers SOC 2 for fintech"), but treat it as a way to surface candidates, then confirm each candidate against Drive before the agent quotes it. The index proposes; Drive decides.
This is a deterministic pipeline, not an open-ended reasoning loop. Every rep request flows through the same four gates, and each gate maps to a concrete Scalekit Google Drive tool or parameter.
The authorization gate is not a step you write; it is a property of the credential. Because the connected account is the rep's OAuth grant, Drive applies the rep's sharing rules to every result. The canonical and verification gates are the code you do write, and they are where "latest" stops being a guess.
The Scalekit Google Drive connector uses OAuth 2.0. Scalekit is the OAuth client: it runs the redirect, obtains the rep's access token, stores it, and refreshes it. Your agent passes a connection_name and a per-rep identifier; it never handles a token.
Each rep who connects gets their own consent flow and their own grant. Forty reps means forty connected accounts, each scoped to what that rep can see in Drive, with no shared credential in the middle.
Before the agent runs, retrieve the tools this rep's connected account authorizes. This is not tool discovery over an unknown surface; it is the fixed, per-rep set that the rep's grant permits. For Google Drive it is exactly three tools.
The scoping that matters for a Drive agent is authorization scoping, not catalog reduction. Scope here is a function of the rep's identity, not of connector configuration: what the rep can't do, the agent can't do. The contrast is concrete inside Scalekit's own catalog. There is a Google Drive connector (per-user OAuth 2.0) and a separate Google Workspace connector that uses a service account with domain-wide delegation. The domain-wide-delegation path authenticates once as an application that can impersonate any user in the workspace; Google's own guidance is to prefer per-user consent precisely because a single delegated key can read every user's Drive. For a multi-rep, multi-account sales agent, per-user connected accounts keep each rep's blast radius to their own grant.
If your sales enablement agent also reaches CRM and messaging (Salesforce, HubSpot, Gong, Attio, Slack are all connectors in the same catalog), the scoped surface also keeps each run down to the tools that user authorized, rather than a combined catalog the model has to select from. With three Drive tools that effect is small; across five connectors it is the difference between a clean decision space and a bloated one. This per-rep scoping approach connects directly to the broader challenge of tool calling auth in multi-tenant environments, where shared credentials compound across every connected system.
Drive gives you no native "approved" flag, and two of its defaults work against you. files.list returns trashed files unless you exclude them, and modifiedTime is the last edit by anyone, so a colleague who opens a superseded draft and saves it pushes that draft above the real, approved file. So encode canonical status somewhere durable (a dedicated approved-collateral folder, an appProperties key such as status=approved, or a Drive label) and force every search onto that contract.
Wrap googledrive_search_files so the contract cannot be bypassed by the model. The wrapper keeps the tool's name and schema, so the LLM sees the same tool; it only rewrites the arguments on the way through.
The canonical query narrows the field to approved, non-trashed files, newest first. The last gate confirms the top candidate is genuinely current before the agent quotes it. Ask only for the fields that decide "current and approved," which keeps the payload small and the check explicit.
Assemble the per-rep, guarded tools and hand them to LangChain's create_agent. The system prompt states the contract; the guarded tool enforces it. Build the tool list per rep so the agent always runs against the right connected account.
amelia and diego hit the same code and the same connection, but their results come through different grants. If a file lives outside diego's sharing, diego's connected account never returns it, and no prompt can talk the agent past that boundary.
This pattern mirrors the architecture described in LangChain tool calling: how it works, where it stops, and how Scalekit completes it — the framework gives you the loop and the schema binding, but credential lifecycle and per-user scoping require the layer on top.
Live per-user tool calls fix freshness and authorization, but they hand you a credential-lifecycle problem that neither a vector-sync job nor hand-rolled OAuth solves for you. Forty reps is forty Drive grants to store encrypted, refresh, and revoke. Google access tokens are short-lived (on the order of an hour), so a real agent refreshes constantly; miss a refresh and searches fail silently mid-conversation rather than erroring. And offboarding is where this bites: a rep leaves, their SSO is disabled, but a Drive grant issued months ago is still valid until something explicitly revokes it. The agent does not decide to keep using it; it just does.
The alternative that appears to sidestep per-rep tokens — a domain-wide-delegation service account — trades forty small grants for one key that can read every rep's Drive. That is a larger blast radius, not a smaller operational surface. Handling token refresh for AI agents at scale requires proactive rotation and a vault that lives outside the agent runtime — exactly what Scalekit's Google Drive connector provides: it runs each rep's OAuth flow, holds each token in the token vault outside your agent runtime, refreshes proactively, and gives you a revocation point per connected account. The credentials never touch the agent; the agent calls a tool and gets a result.
The offboarding gap is a real operational risk. As covered in when an employee leaves, who revokes their AI agent's access, a disabled SSO account does not automatically invalidate OAuth grants issued to autonomous agents — that revocation has to be explicit and tied to a real identity lifecycle event.
For a deeper look at how credential ownership across agent tool-calling patterns shapes your risk surface, the ownership question — does the agent hold the token, or does an intermediary — determines whether a single key failure cascades or stays contained.
It works, and it is one connection to manage, but the access model is wrong for a multi-rep agent. A domain-wide-delegation key can impersonate any user in the workspace, so it can read every rep's and every account's Drive; Google's own guidance is to prefer per-user consent for exactly this reason. Per-user connected accounts keep each rep's reach to their own grant, which is also what makes the authorization gate free: the agent inherits the rep's sharing rules instead of an admin-level view.
No, and this is the most common way these agents ship a wrong answer. modifiedTime is the last edit by anyone, so someone opening and saving a superseded draft ranks it above the approved file. Recency ordering only means "newest edit," not "current approved version." That is why the contract pairs the ordering with an approved-folder or appProperties predicate and a googledrive_get_file_metadata check before the agent quotes anything.
Only for semantic recall, and never as the authority. If reps ask by meaning ("the deck that handles security objections") rather than by name, an index earns its place as a candidate finder. Resolve those candidates to live file_ids and run them through the canonical and verification gates so the answer reflects Drive's current, ACL-filtered state, not the index's last sync. The index proposes; Drive decides.
googledrive_search_files and googledrive_get_file_metadata both accept supports_all_drives. Set it when your approved collateral lives in a shared drive, and keep the same approved-folder or appProperties predicate so the canonical contract still holds across drives.
Two layers. The guarded search restricts results to approved, non-trashed files, so a draft outside the approved folder never enters the candidate set. The system prompt then requires a googledrive_get_file_metadata verification before the agent cites anything, and instructs it to say no approved file matches rather than fall back to an unapproved copy.
Nothing you have to handle in the agent. Scalekit refreshes the rep's Drive token behind the connected account, so the next googledrive_search_files or googledrive_get_file_metadata call runs on a valid credential. Your code passes a connection_name and identifier; it never sees or refreshes the token itself. This is the same secure token management model described in secure token management for AI agents at scale — tokens live in a vault, the agent never holds them, and refresh is invisible to your code.