# Google Docs and Drive

> Import a reviewed copy of a Google Doc, PDF, or Word file as a Korala document or template.

Source: https://docs.korala.ai/guides/google-workspace

---

Choose **Import from Google Drive** in **Documents** or **Templates**. Connect Google if prompted, then select a Google Doc, Word file, or PDF. The page you started from determines whether the import creates a document or template. Manage connected accounts in **Settings → Integrations → Google Docs and Drive**. Korala prepares a PDF for review before creating the resource. Creating a document leaves it in draft; add signers and fields before sending it.

Choose optional destination collections before preparing the preview. Starting from a collection's **Import from Google Drive** action preselects that collection; you can change or clear the selection. Test collections create test resources. Korala checks destinations again when you create the reviewed resource. If a collection was archived during preparation, restore it before retrying publication, or start a new import with another destination.

A Google connection belongs to the user who connected it. Korala requests `drive.file` access to files selected for or created by the application. Connecting Google does not grant access to your entire Drive or give every API key access to your files.

## Documents and templates

For a native Google Doc without variables, a document import uses Google's PDF export. A template import retains the content of the exported Word file and renders its preview using Korala's converter. Documents with variables use that same Word conversion after values are supplied. PDF and Word files stored in Drive can also be imported.

Google and Korala can paginate the same source differently. For native Google Docs, Korala suppresses blank parity pages automatically inserted by the Word converter. Tab-title pages and explicit page breaks in Google’s export are preserved. This policy is saved as `googleSource.skipEmptyPages` (or `provenance.skipEmptyPages` on an import); existing imports without it retain their original pagination so placed fields do not shift. Reconfirming a template or replacing its Word file retains this saved rendering policy for both the preview and generated documents. Review every page, including tables, images, headers, footers, and content from other tabs. Place signing fields on the final PDF after values have been filled.

Use simple variables such as `{{client_name}}`. You can include spaces around the name, as in `{{ client_name }}`. Repeated variables use the same value. Expressions, loops, and literal double braces are not supported in this import workflow. Resolve suggested edits in Google Docs before importing.

Supply a string value for each detected variable when importing a document. An empty string is an explicit blank; omitting a value is an error. Template imports retain variables for later use. Generating from a Google-derived Word template uses the same explicit-value validation and expansion limits; choose **Leave blank** for intentional blanks. Duplicating a template preserves its Google source history and these validation rules.

This version creates a copy. Editing Google Docs later does not update that copy. Template refresh, automatic synchronization, a Google Docs add-on, and writing signed files back to Drive are separate follow-up work.

## API workflow

The connection owner must first connect Google, choose the source file, select the key under **Optional partner API access**, and click **Allow access**. The page shows the connection and file IDs needed by the client. **Remove access** revokes that grant. OAuth connection and grant management require an interactive user session; API keys cannot create connections or grant themselves access.

The TypeScript client exposes the import workflow through `client.googleWorkspace`:

```typescript
const operation = await client.googleWorkspace.createImport({
  connectionId,
  fileId,
  target: 'document', // or 'template'
  name: 'Services agreement',
  idempotencyKey: crypto.randomUUID(),
});

let current = await client.googleWorkspace.getImport(operation.id);

if (current.status === 'awaiting_values') {
  current = await client.googleWorkspace.supplyValues(current.id, {
    values: { client_name: 'Example Company', reference: '' },
  });
}
```

Poll `getImport` while the status is `queued` or `processing`, using a delay between requests. When it becomes `ready`, display `previewUrl` for review. After the user confirms that exact PDF, pass its `reviewHash`:

```typescript
const completed = await client.googleWorkspace.publishImport(current.id, {
  reviewHash: current.reviewHash!,
});
// completed.documentId or completed.templateId identifies the created resource.
```

Wait for the user to review the PDF before publishing an import. The hash binds confirmation to the prepared artifact; a stale hash returns `409`. Repeating publication with the same hash returns the existing resource.

Reuse the creation idempotency key only when retrying the same request. Changing the file, target, name, API key, or sandbox mode with that key returns `409`.

The public import response includes source provenance, detected variables, status, and the created resource ID. Provenance records the source file and version, fetch time, variable dialect, and SHA-256 hashes of the original Google download, stored source, and reviewed PDF. Word imports rebuild the ZIP container from validated entries; the stored Word file can have different bytes while preserving its XML and media content. Document and template detail responses also expose this metadata as `googleSource`. These responses omit Google credentials and storage keys.

The import request accepts optional `collectionIds` (up to 100 distinct collection UUIDs). Omission or `[]` leaves the resource unassigned. The operation response retains the requested destinations; the created resource's details show its current memberships. Dashboard requests can supply `sandbox`; API keys always use their authenticated mode. Destinations must be active and belong to the same organization and mode. Collection assignment and resource creation commit together. Reusing an idempotency key with different destinations returns `409`; destination ordering and UUID letter casing do not matter.

## Limits and failure handling

Imports accept at most 10 MiB per downloaded artifact, subject to the plan's lower upload limit. PDFs must be readable, unencrypted, and no longer than 500 pages. Word archives are limited to 5,000 entries and 64 MiB expanded content, with at most 200,000 XML markup tokens before parsing. Sources may contain up to 500 distinct variables, and supplied values may total up to 100,000 characters. Values multiplied by their occurrence counts must stay within 8 MiB of text. Complex source trees and text matching also have memory bounds.

If Google changes the source during export, the import fails with `source_changed`. Wait for editing to finish and create a new import. `export_content_mismatch` means the exported Word content did not pass the source-content checks. Neither an unchanged Google version nor a successful content check guarantees visual fidelity.

Disconnecting Google prevents further import work through that connection. Revoking a partner grant prevents that API key from importing the source or publishing pending imports. Copies already created in Korala remain available. Removing the connection owner from the organization removes their Google credentials and stops pending imports; published copies retain their source history.

## Self-hosted configuration

Enable the Google Drive, Docs, and Picker APIs in the same Google Cloud project. Create a web OAuth client with the dashboard origin and its `/settings/google` callback URL. Configure:

| Variable | Purpose |
| --- | --- |
| `GOOGLE_WORKSPACE_CLIENT_ID` | Web OAuth client ID, used by API and worker |
| `GOOGLE_WORKSPACE_CLIENT_SECRET` | OAuth client secret, used by API and worker |
| `GOOGLE_WORKSPACE_REDIRECT_URI` | Dashboard `/settings/google` callback URL |
| `GOOGLE_WORKSPACE_ENCRYPTION_KEY` | Dedicated 32-byte key encoded as 64 hexadecimal characters |
| `NEXT_PUBLIC_GOOGLE_PICKER_API_KEY` | Browser API key restricted to Picker and approved website referrers |
| `NEXT_PUBLIC_GOOGLE_WORKSPACE_PROJECT_NUMBER` | Numeric Google Cloud project number for Picker |
| `GOTENBERG_URL` | Reachable conversion service URL, required by both API and worker |

Keep server secrets out of browser configuration. The encryption key must be identical in API and worker deployments and preserved across restarts. Changing it requires reconnecting existing Google accounts. Add your test users to OAuth applications in testing mode.

### Production Google Cloud setup

Use one Google Cloud project for the OAuth client, Picker API key, and project number. Enable **Google Drive API**, **Google Docs API**, and **Google Picker API**.

In **Google Auth Platform**, configure the app name, support email, homepage, privacy policy, terms, and authorized domain. For a public service, use an **External** audience and publish the consent configuration to **In production**. Complete any branding verification Google requests. Korala requests only `openid`, `email`, and `https://www.googleapis.com/auth/drive.file`; do not add whole-Drive or whole-Docs scopes. Google classifies `drive.file` as non-sensitive. Workspace administrators can still restrict third-party apps.

For Korala's production web OAuth client, configure:

- Authorized JavaScript origin: `https://korala.ai`.
- Authorized redirect URI: `https://korala.ai/settings/google`, exactly matching `GOOGLE_WORKSPACE_REDIRECT_URI`.
- If the dashboard is also served on another hostname, explicitly configure that origin and its callback, and keep the callback on the same host as the user's session. Prefer one canonical dashboard hostname.

Restrict the browser API key to the **Google Picker API** and HTTP referrers `https://korala.ai/*` and `https://docs.google.com/*`. Google's Picker runs in an iframe on `docs.google.com` and requires that referrer too. Add other dashboard hosts only if they are actually served. Use separate development credentials/referrer allowances for localhost.

Set the four `GOOGLE_WORKSPACE_*` variables on both API and worker. Generate a dedicated encryption key once and store it securely; API and worker must share the same value. Set both `NEXT_PUBLIC_*` variables from the table on the web service **before building it**; rebuild the web image after changing them. Google Workspace credentials are separate from the `GOOGLE_CLIENT_*` credentials used for signing in to Korala.

For Render, a shared environment group can hold the four server variables and be linked only to the API and worker. Generate the encryption key with `openssl rand -hex 32` and verify that it contains 64 hexadecimal characters; Render's Generate button may produce a shorter value. Set `GOTENBERG_URL` on the worker as well as the API. Saving or linking configuration without deploying stages it for the next deployment; it does not update running services.

Apply the database migration before enabling import traffic, then deploy the API, worker, and web together. Verify a fresh connection on the production hostname, selecting an existing file through Picker, importing and reviewing a draft, reusing a template, and disconnecting/reconnecting. Check refresh-token use after the initial access token expires. External apps left in Testing issue refresh tokens that expire after seven days for these scopes; reconnect any test accounts as needed after publishing.

See Google's [Picker setup](https://developers.google.com/workspace/drive/picker/guides/web-picker), [Drive scope classifications](https://developers.google.com/workspace/drive/api/guides/api-specific-auth), and [OAuth token expiration rules](https://developers.google.com/identity/protocols/oauth2#expiration).
