Google Drive Integration
StudioBrain can sync your content directly to Google Drive, allowing you to:
- Store all entity markdown files in Google Drive
- Access your content from any device with Drive access
- Collaborate using Drive’s sharing features
- Backup automatically with Drive’s version history
Overview
The Google Drive integration uses the Google Drive API v3 to:
- Create folder structures matching StudioBrain’s entity organization
- Store entity markdown as text files
- Manage assets (images, audio, video) in subfolders
- Sync changes using the Drive Changes API
- Provide temporary download URLs for assets
Prerequisites
Before setting up Google Drive integration:
- Google Cloud Project with Drive API enabled
- OAuth 2.0 credentials (web application type)
- Drive folder to use as root for StudioBrain content
Setting Up Google Cloud Project
- Go to Google Cloud Console
- Create a new project (or use existing)
- Enable Google Drive API
- Go to APIs & Services > OAuth consent screen
- Choose “External” user type
- Fill in required app information
- Add scopes:
https://www.googleapis.com/auth/drive.file
- Go to APIs & Services > Credentials
- Create OAuth 2.0 Client ID (Web application)
- Add authorized redirect URIs (StudioBrain will provide)
- Save the Client ID and Client Secret
Authentication Flow
OAuth 2.0 Flow (Recommended)
StudioBrain uses OAuth 2.0 to authenticate with Google Drive on behalf of users:
-
Authorization Request
- User clicks “Connect Google Drive” in StudioBrain
- Cloud generates a state token and stores OAuth context in Valkey (10-min TTL)
- Redirected to Google OAuth consent screen
- User grants permission for StudioBrain to access Drive
-
Token Exchange
- Google redirects back with authorization code
- Cloud looks up the state from Valkey, validates it, and deletes it
- Cloud sends the code to the accounts service (
POST /internal/storage-oauth/gdrive/exchange) - Accounts service exchanges the code for access/refresh tokens and persists them encrypted on the User model
-
Token Refresh
- Access tokens expire after 1 hour
- StudioBrain automatically refreshes using refresh token
- Refresh tokens don’t expire (until user revokes)
Architecture note (SBAI-2387): OAuth token persistence is handled exclusively by the accounts service. Cloud never writes tokens directly — it delegates to
POST /internal/storage-oauth/{provider}/exchangewhich encrypts and stores tokens on the User model. This ensures OAuth tokens (PII) are written only by the service authorized to manage user credentials.
Token Storage
OAuth tokens are stored encrypted on the User model in the accounts service database:
{
"google_drive": {
"root_folder_id": "abc123xyz",
"root_folder_name": "StudioBrain Content",
"connected_at": "2026-03-10T10:00:00Z",
"connected_by": "user-uuid"
}
}The actual access/refresh tokens (google_access_token, google_refresh_token, google_token_expires) are stored as encrypted columns on the User record and are never exposed in API responses or tenant config.
OAuth Through Google Sign-In
OAuth for Drive access is handled through the main Google Sign-In flow:
- Incremental Authorization:
GET /api/auth/oauth/google/drive - This endpoint provides the OAuth URL to grant Drive-specific permissions
- After authorization, tokens are stored on the User model
Folder Structure
StudioBrain mirrors its local filesystem structure on Google Drive:
Drive Root (specified during setup)
├── Characters/
│ ├── john_doe/
│ │ ├── CH_john_doe.md # Main entity file
│ │ ├── images/
│ │ │ ├── portrait.png
│ │ │ └── concept_art.jpg
│ │ ├── audio/
│ │ │ └── voice_sample.wav
│ │ └── models/
│ └── jane_smith/
│ └── CH_jane_smith.md
├── Locations/
│ ├── neotokyo/
│ │ └── LOC_neotokyo.md
│ └── space_station/
│ └── LOC_spacestation.md
├── Items/
├── Factions/
├── Dialogues/
└── Assets/
└── shared_resources/Entity Type Folders
Each entity type gets a top-level folder:
CharactersLocationsItemsFactionsJobsBrandsDialoguesWorkflowsShowsCampaignsMusicAudioSocialMediaToolsGDDStyleGuideScriptsTimelineAssemblyInventoryArchives
Asset Subfolders
Each entity folder can contain these asset subfolders:
images- PNG, JPG, GIF, WebPaudio- MP3, WAV, OGGvideo- MP4, WebMmodels- GLB, GLTF, FBXdata- JSON, CSV, XMLcomfy_workflows- YAML workflow files
Extra Folders by Type
Some entity types get additional folders:
- Locations:
maps/for location maps
Setup Instructions
1. Configure OAuth Credentials
In StudioBrain admin panel:
- Go to Settings > Integrations > Google Drive
- Enter your Google Cloud credentials:
- Client ID
- Client Secret
- Redirect URI (provided by StudioBrain)
- Save configuration
2. Connect Google Drive
- Go to Settings > Integrations > Google Drive
- Click Connect to Google Drive
- Authorize StudioBrain in the Google popup
- Select the Drive folder to use as root using the folder picker
- Click Select Folder
3. Enable Cloud Sync
- Go to Settings > Sync
- Enable Cloud Sync
- Select Google Drive as storage provider
- Configure sync frequency (real-time or manual)
Usage
Creating Entities
When you create a new entity:
- StudioBrain creates the entity folder on Drive
- Standard asset subfolders are created automatically
- The main markdown file is created with entity frontmatter
- Content is saved to Drive on every save
Syncing Changes
StudioBrain uses Drive’s Changes API to detect changes:
- Polling: StudioBrain polls Drive for changes every N minutes
- Start Page Token: Stored to resume from last known state
- Change Events:
created- New file/folder detectedupdated- File modifieddeleted- File/folder trashed
Asset Management
Assets are uploaded to Drive as binary files:
- Upload: Asset data sent via multipart upload
- URL Generation: Drive provides temporary webContentLink
- Caching: URLs cached for performance
- Cleanup: Orphaned assets detected and cleaned
Conflict Resolution
When conflicts occur (local vs Drive):
- Timestamp check: Most recent wins
- Manual resolve: User prompted if conflicts persist
- Version history: Drive keeps versions for 30 days
API Reference
Connection Status Endpoints
Check Drive Connection Status
GET /api/drive/status
Check if Google Drive is connected and the user has valid tokens.
Response:
{
"connected": true,
"configured": true,
"has_access_token": true,
"auth_provider": "google",
"token_expires": "2026-03-10T15:30:00Z",
"upgrade_url": "/api/auth/oauth/google/drive"
}Response Fields:
| Field | Type | Description |
|---|---|---|
connected | boolean | True when user has stored Google access token |
configured | boolean | True when Google OAuth is configured on server |
has_access_token | boolean | Whether user has valid tokens |
auth_provider | string | OAuth provider (e.g., “google”) |
token_expires | string | ISO 8601 token expiration timestamp |
upgrade_url | string | URL to start drive.file upgrade flow (null if not configured) |
Drive Storage Provider Endpoints
These endpoints manage Google Drive as a storage provider for the tenant.
Check Storage Provider Status
GET /api/drive-storage/status
Return the Google Drive storage provider status for the current tenant.
Response:
{
"server_configured": true,
"user_has_tokens": true,
"is_active_provider": true,
"root_folder_id": "abc123xyz",
"root_folder_name": "StudioBrain Content",
"connected_at": "2026-03-10T10:00:00Z",
"connected_by": "user-uuid",
"login_url": "/api/auth/oauth/google",
"upgrade_url": "/api/auth/oauth/google/drive"
}Response Fields:
| Field | Type | Description |
|---|---|---|
server_configured | boolean | True when Google OAuth is configured on server |
user_has_tokens | boolean | Whether user has Drive tokens |
is_active_provider | boolean | True when Drive is the active storage provider |
root_folder_id | string | Drive folder ID used as project root |
root_folder_name | string | Display name of the root folder |
connected_at | string | ISO 8601 timestamp when connected |
connected_by | string | User ID who connected |
login_url | string | URL to sign in with Google |
upgrade_url | string | URL to grant Drive access |
List Drive Folders for Picker
GET /api/drive-storage/folders
List folders on the user’s Google Drive for the folder picker UI.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
parent_id | string | null | Parent folder ID (omit for root) |
page_token | string | null | Pagination token |
page_size | integer | 50 | Max results (1-200) |
Response:
{
"folders": [
{
"id": "folder123",
"name": "StudioBrain Content",
"modified_time": "2026-03-09T14:20:00Z"
},
{
"id": "folder456",
"name": "Characters Backup",
"modified_time": "2026-03-08T10:00:00Z"
}
],
"next_page_token": "CgkIAhIFGNnQ8wMSFQoNCPqL9tICEgkI9ov2wgISAggBGAEgAQ==",
"parent_id": "root"
}Example:
curl -X GET "http://localhost:8201/api/drive-storage/folders" \
-H "Authorization: Bearer YOUR_TOKEN"Select Project Folder
POST /api/drive-storage/select-folder
Select a Google Drive folder as the project root and activate Drive as the storage provider.
Request Body:
{
"folder_id": "abc123xyz",
"folder_name": "StudioBrain Content"
}Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
folder_id | string | Yes | Google Drive folder ID |
folder_name | string | No | Display name of the folder (for UI) |
Response:
{
"status": "connected",
"folder_id": "abc123xyz",
"folder_name": "StudioBrain Content",
"provider": "google_drive"
}Example:
curl -X POST "http://localhost:8201/api/drive-storage/select-folder" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"folder_id": "abc123xyz",
"folder_name": "StudioBrain Content"
}'Disconnect Drive Storage
POST /api/drive-storage/disconnect
Disconnect Google Drive as the storage provider, reverting to local_fs.
Response:
{
"status": "disconnected",
"provider": "local_fs"
}Test Drive Connection
POST /api/drive-storage/test
Test the Google Drive connection by listing the root folder contents.
Response:
{
"status": "ok",
"root_folder_id": "abc123xyz",
"root_folder_name": "StudioBrain Content",
"folders_found": 5,
"files_found": 127,
"folder_names": ["Characters", "Locations", "Items", "Factions", "Dialogues"]
}Example:
curl -X POST "http://localhost:8201/api/drive-storage/test" \
-H "Authorization: Bearer YOUR_TOKEN"Register Desktop Tokens
POST /api/drive-storage/register-desktop-tokens
Accept Google Drive OAuth tokens pushed from a desktop client and store them in the tenant’s storage config.
This is used when a desktop user completes Google Drive OAuth, pushing tokens to the SaaS backend for cloud sync.
Request Body:
{
"access_token": "ya29.a0AeDClZ...",
"refresh_token": "1//0gHwQ...",
"token_expires": "2026-03-10T15:30:00Z",
"root_folder_id": "abc123xyz",
"root_folder_name": "StudioBrain Content"
}Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
access_token | string | No | Google OAuth access token (fallback to user tokens) |
refresh_token | string | No | Google OAuth refresh token |
token_expires | string | No | ISO 8601 expiration timestamp |
root_folder_id | string | Yes | Google Drive folder ID |
root_folder_name | string | No | Display name of the folder |
Response:
{
"status": "registered",
"folder_id": "abc123xyz",
"folder_name": "StudioBrain Content",
"provider": "google_drive",
"source": "desktop"
}Cloud Sync Endpoints
Note: These endpoints are only available in cloud deployments.
Check Cloud Sync Status
GET /api/drive-storage/cloud-sync-status
Return the cloud Drive sync worker status including desktop presence.
Response:
{
"sync_worker": {
"running": true,
"last_sync": "2026-03-10T10:30:00Z",
"entities_synced": 1247,
"assets_synced": 3456,
"last_error": null
},
"desktop_presence": {
"online": true,
"sessions": 1
},
"token_health": {
"healthy": true,
"last_refresh": "2026-03-10T10:25:00Z",
"refreshes_remaining": 45
}
}Response Fields:
| Field | Type | Description |
|---|---|---|
sync_worker | object | Worker statistics (null if not running) |
desktop_presence | object | Whether desktop client is online |
token_health | object | OAuth token status |
Force Drive Sync
POST /api/drive-storage/force-sync
Trigger an immediate Drive sync for the current tenant, bypassing the desktop-online check.
Response:
{
"status": "completed",
"results": {
"entities_created": 2,
"entities_updated": 5,
"entities_deleted": 0,
"assets_synced": 12
}
}Drive File Operations Endpoints
These endpoints provide direct file operations on Google Drive.
Note: Write operations (export-entity, update, share) require drive.file scope. Users without write scope receive a 403 with an upgrade_url.
List Drive Files
GET /api/drive/files
List files from the user’s Google Drive.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | null | Drive API query string |
folder_id | string | null | Restrict to folder ID |
page_token | string | null | Pagination token |
page_size | integer | 20 | Max results (1-100) |
Response:
{
"files": [
{
"id": "file123",
"name": "CH_john_doe.md",
"mimeType": "text/markdown",
"size": "4096",
"modifiedTime": "2026-03-10T09:15:00Z",
"webContentLink": "https://..."
}
],
"nextPageToken": "CgkIAhIFGNnQ8wMSFQoNCPqL9tICEgkI9ov2wgISAggBGAEgAQ=="
}Get Drive File Metadata
GET /api/drive/files/{file_id}
Return metadata for a specific Google Drive file.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
file_id | string | Google Drive file ID |
Response:
{
"id": "file123",
"name": "CH_john_doe.md",
"mimeType": "text/markdown",
"size": "4096",
"createdTime": "2026-03-09T10:00:00Z",
"modifiedTime": "2026-03-10T09:15:00Z",
"webLink": "https://drive.google.com/file/d/file123/view",
"webViewLink": "https://drive.google.com/file/d/file123/view",
"description": "Character profile for John Doe"
}Export Drive File Content
GET /api/drive/files/{file_id}/export
Export a Google Workspace document or regular text file as plain text.
Google Docs are exported via the Drive /export API. Other file types are downloaded directly via ?alt=media.
Response: text/plain
---
type: Character
name: John Doe
---
Character content here...Export Entity to Drive
POST /api/drive/export-entity
Create a new Google Doc containing the supplied entity content.
Request Body:
{
"name": "John Doe Character Profile",
"content": "---\ntype: Character\nname: John Doe\n...\n\nCharacter content here.",
"folder_id": "abc123xyz" // optional
}Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Document title |
content | string | Yes | Plain-text or Markdown content |
folder_id | string | No | Optional parent folder ID |
Response:
{
"file_id": "xyz789abc",
"name": "John Doe Character Profile",
"web_view_link": "https://drive.google.com/file/d/xyz789abc/view",
"mime_type": "application/vnd.google-apps.document"
}Update Drive File Content
PUT /api/drive/files/{file_id}
Overwrite the content of an existing Google Drive file.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
file_id | string | Google Drive file ID |
Request Body:
{
"content": "---\ntype: Character\nname: John Doe\n...\n\nUpdated character content."
}Response:
{
"file_id": "file123",
"name": "CH_john_doe.md",
"modified": true,
"modified_time": "2026-03-10T11:30:00Z"
}Share Drive File
POST /api/drive/files/{file_id}/share
Share a Google Drive file with another user by email address.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
file_id | string | Google Drive file ID |
Request Body:
{
"email": "colleague@example.com",
"role": "writer"
}Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Recipient email address |
role | string | No | Default: “reader”. One of: “reader”, “commenter”, “writer” |
Response:
{
"permission_id": "perm123",
"email": "colleague@example.com",
"role": "writer"
}Troubleshooting
OAuth Connection Fails
Error: OAuth authorization failed
Possible Causes:
- Client ID/Secret incorrect
- Redirect URI mismatch
- OAuth consent not configured
Solutions:
- Verify Client ID and Secret in Google Cloud Console
- Add StudioBrain’s redirect URI to authorized list
- Complete OAuth consent screen setup
Drive Folder Not Found
Error: Root folder not found on Drive
Possible Causes:
- Folder was deleted
- Folder ID changed
- Permission issue
Solutions:
- Reconnect Google Drive and select the correct folder
- Verify Drive folder still exists
- Check folder permissions
Sync Failures
Error: Failed to sync entity to Drive
Possible Causes:
- Network connectivity issue
- Drive API quota exceeded
- Invalid entity ID or content
Solutions:
- Check network connectivity
- Review Drive API quota in Google Cloud Console
- Verify entity content is valid markdown
- Force sync:
POST /api/drive-storage/force-sync
Token Refresh Failed
Error: Token refresh failed (401)
Possible Causes:
- Refresh token expired
- User revoked access
- Client credentials changed
Solutions:
- Reconnect Google Drive (re-authorize)
- Verify Client credentials haven’t changed
- Check if user revoked access in Google account
Insufficient Permissions
Error: Insufficient Drive scope
Possible Causes:
- Token doesn’t have drive.file scope
- Drive API not enabled on project
Solutions:
- Re-authenticate:
GET /api/auth/oauth/google/drive - Verify Drive API is enabled in Google Cloud Console
- Grant Drive permissions during OAuth flow
Security Considerations
Data Encryption
- At Rest: Drive stores data encrypted
- In Transit: All Drive API calls use HTTPS
- Tokens: OAuth tokens encrypted in database
Permission Scopes
StudioBrain requests minimal scopes:
https://www.googleapis.com/auth/drive.file- Create, read, update, delete own files
Access Control
- Each tenant has separate Drive folder
- Tenant tokens stored separately
- No cross-tenant data access
Backup and Recovery
- Drive keeps file versions for 30 days
- Use Drive’s version history to restore
- StudioBrain maintains local cache as backup
Best Practices
- Use dedicated folder: Create a separate Drive folder for StudioBrain
- Limit scope: Drive API scope is limited to StudioBrain files only
- Monitor quota: Watch Drive API usage in Google Cloud Console
- Regular backups: Enable Drive version history
- Test connection: Use
POST /api/drive-storage/testregularly
Related Documentation
- Cloud Sync - Overview of sync features
- Self-Hosting - Drive integration on self-hosted
- Google Drive API Docs - Official Google Drive API