Google Drive (headless, no per-user login)

Captain can index Google Drive without every user clicking “Allow.” Instead of per-user OAuth, your Google Workspace admin authorizes a single service account once. Captain then reads Drive as a specific user you name on each request — a single file, one folder (recursively), or that user’s entire Drive.

This is the pattern to use when you want to index Drive content at scale, on a schedule, or on behalf of many users, without a login step in the loop.

How it works

  1. You create a service account in Google Cloud and download its JSON key.
  2. Your Workspace super admin authorizes that service account for read-only Drive access, domain-wide (a one-time grant in the Admin console).
  3. On each indexing request you send the service-account key plus a subject_email — the Workspace user whose Drive Captain should read. Captain mints a short-lived token as that user and reads only what that user can see.

Nothing durable is stored on Captain’s side beyond the request; the token is minted per job and discarded.

One-time setup

Every step below is done by someone with Google Workspace super administrator rights on your domain. It takes about 15 minutes and is done once — after that, indexing is a plain API call.

Step 1 — Create a service account and key

You need a Google Cloud project to hold the service account. If your organization doesn’t use Google Cloud, that’s fine — any Google account can create a project for free, and this use case requires no billing.

  1. Go to console.cloud.google.com.
  2. Create a project (or pick an existing one). Top bar → project picker → New Project → name it e.g. captain-drive-indexingCreate.
  3. Enable the Drive API: APIs & Services → Enable APIs and services → search “Google Drive API”Enable.
  4. Create the service account: IAM & Admin → Service Accounts → Create service account. Give it a name (e.g. captain-indexing) → Create and continue → skip the optional roles → Done. (No project roles are needed — this account’s access comes entirely from the Workspace delegation grant in Step 2.)
  5. Create a key: open the service account → Keys tab → Add key → Create new key → JSON → Create. A .json file downloads. This is the service_account_json you’ll send to Captain — keep it secret.
  6. Note the service account’s numeric Client ID (also called the “Unique ID” or “OAuth2 Client ID”). You’ll need it in Step 2. Find it on the service account’s Details tab, or under IAM & Admin → Service Accounts (the “OAuth 2 Client ID” column).

Google does not offer a way to create a service account entirely inside the Admin console — the account itself must be created in Google Cloud, as above. Only the delegation grant (Step 2) lives in the Admin console.

Step 2 — Authorize the service account (domain-wide delegation)

This is the grant that lets Captain read Drive as your users. It’s done in the Admin console (admin.google.com), which is a different console from the Cloud one in Step 1.

  1. Go to admin.google.comSecurity → Access and data control → API controls.
  2. In the Domain-wide delegation panel, click Manage Domain-Wide Delegation.
  3. Click Add new and enter:
    • Client ID — the numeric Client ID from Step 1 (not the service account’s email address).
    • OAuth scopes — exactly:
      https://www.googleapis.com/auth/drive.readonly
  4. Click Authorize.

Grant only drive.readonly. Captain never writes to or deletes from Drive. Scoping to read-only keeps the grant safe and speeds any internal security review.

The grant can take a few minutes to propagate (occasionally up to ~30). If your first indexing call returns unauthorized_client, wait and retry.

Choosing which user to index (subject_email)

Captain reads Drive as the subject_email you pass, so pick the user whose content you want:

  • A specific person’s Drive → their email (e.g. analyst@yourco.com).
  • A Shared Drive / team content → any user who is a member of that Shared Drive. Files that user can’t see are simply skipped.
  • An org-wide “service” identity → many teams create a dedicated Workspace user (e.g. indexing@yourco.com), share the relevant Drives and folders with it, and always impersonate that one account. This keeps the indexed scope explicit and easy to audit.

Indexing

All three endpoints take the same auth fields — the service-account key and the user to impersonate — and differ only in scope.

A single file

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/gdrive/file" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "service_account_json": "<full contents of the JSON key file, as a string>",
> "subject_email": "analyst@yourco.com",
> "file_id": "1AbC...",
> "processing_type": "advanced"
> }'

file_id is the /d/<id>/ segment of a Drive file URL.

A folder (recursively)

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/gdrive/directory" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "service_account_json": "<JSON key as a string>",
> "subject_email": "analyst@yourco.com",
> "folder_id": "1MHp...",
> "processing_type": "advanced"
> }'

folder_id is the trailing segment of a folder URL (drive.google.com/drive/folders/<folder_id>). Subfolders are indexed too.

An entire Drive

$curl -X POST "https://api.runcaptain.com/v2/collections/{collection_name}/index/gdrive" \
> -H "Authorization: Bearer $CAPTAIN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "service_account_json": "<JSON key as a string>",
> "subject_email": "analyst@yourco.com",
> "processing_type": "advanced"
> }'

Every response is { "job_id": "...", "status": "pending" }. Poll GET /v2/collections/{name}/jobs/{job_id} for progress, then query the collection with POST /v2/collections/{name}/query.

Passing the key JSON. service_account_json is the entire contents of the .json key file, as a single JSON string. In most languages, read the file and JSON-encode its text before putting it in the request body.

What gets indexed

  • Regular files (PDF, DOCX, XLSX, CSV, TXT, images, etc.) — downloaded and processed normally.
  • Native Google Docs, Sheets, and Slides — exported automatically before indexing: Docs and Slides → PDF, Sheets → XLSX. You don’t do anything; the exported file is what gets indexed.
  • Shared Drives — included, as long as subject_email is a member.
  • Skipped: Google Forms and Sites (no exportable document body), and any file the subject_email user can’t access (logged and skipped — the job continues).

Troubleshooting

SymptomCause & fix
unauthorized_clientThe delegation grant (Step 2) is missing, still propagating, or the scope doesn’t match. Confirm you entered the numeric Client ID (not the email) and the exact scope https://www.googleapis.com/auth/drive.readonly, then wait a few minutes and retry.
Google Drive delegation failed for <email>Same as above, surfaced per request. Also check the Drive API is enabled on the Cloud project.
A folder/global job finishes with some files failed (403)Those files aren’t shared with subject_email. Share them with that user, or impersonate a user who can see them. Captain skips them and indexes the rest.
Job completed, but a Google Sheet indexed 0 chunksThe sheet is empty (no tabular data). Nothing to index — expected.

Security notes

  • The grant is read-only (drive.readonly) — Captain can never modify or delete Drive content.
  • Captain reads only what the impersonated subject_email can see. To limit scope, impersonate a dedicated user and share only the intended Drives with it.
  • The service-account JSON key is a credential — treat it like a password. You can revoke it any time in Google Cloud (delete the key), or revoke all access by removing the delegation grant in the Admin console.
  • Every read is attributable to the subject_email in your Google audit logs.