When to Use
- Client onboarding — dedicated workbooks: When a new enterprise client signs up, a workflow creates a dedicated Google Sheets workbook named "Acme Corp — Analytics 2026" with pre-defined tabs (Dashboard, Raw Data, Configuration). The spreadsheet URL is stored in the CRM and shared with the client's team via the Google Drive API.
- Annual report provisioning: At the start of each fiscal year, a scheduled workflow creates the new annual report spreadsheet (e.g. "BizFirst KPI Report 2027") with 12 month tabs pre-defined using the
Sheets parameter. All 12 tabs are created in one call, then populated by subsequent workflows.
- Project kick-off automation: When a project is approved, a workflow creates a project-specific tracking spreadsheet with tabs for Budget, Timeline, Risks, and Contacts. The spreadsheet ID is recorded in the project management database for use by all downstream reporting workflows.
- Data export buffers: A data pipeline creates a temporary spreadsheet to stage exported data from a database query before transformation. After the transformation completes and results are pushed to the final destination, the staging spreadsheet is deleted via
spreadsheet/delete.
- Multi-tenant analytics — per-user workbooks: Each user who activates a premium analytics feature gets a dedicated Google Sheets workbook provisioned in their Google Drive (using their OAuth credential). The workbook is created with their name and a pre-structured set of analysis tabs.
Configuration
| Field | Required | Description |
CredentialId | Required | Integer ID referencing a stored Google credential in the BizFirst Credentials Manager. The created spreadsheet will be owned by this Google account (service account or OAuth user). |
Title | Required | The name of the new spreadsheet as it will appear in Google Drive and the Sheets UI. Supports dynamic expressions: {{ $json.clientName }} — Report {{ $now | date('YYYY') }}. Maximum 255 characters. |
Sheets | Optional | List of SpreadsheetSheetDefinition objects to provision as initial tabs. Each definition has: Title (string, tab name) and Hidden (boolean, default false). When omitted, the spreadsheet is created with a single default tab named "Sheet1". Providing this list is more efficient than creating tabs individually with sheet/create after the fact. |
Locale | Optional | BCP 47 locale string, e.g. "en_US", "de_DE", "fr_FR". Controls the spreadsheet's number formatting, date formatting, and currency symbol defaults. When omitted, the Google account's default locale is used. |
RecalculationInterval | Optional | String. Controls how often volatile functions (e.g. NOW(), RAND()) recalculate. Options: "ON_CHANGE" (default — recalculates when the spreadsheet changes), "MINUTE" (recalculates every minute), "HOUR" (recalculates every hour). Use "HOUR" or "ON_CHANGE" for performance — "MINUTE" generates frequent recalculation load. |
Service account file visibility: When a spreadsheet is created by a service account, it is placed in the service account's Google Drive — not in any human user's Drive. To make it accessible to human users, you must share the spreadsheet after creation. Use an HttpRequest node to call the Google Drive API's /permissions endpoint to add the target user's email as an editor or viewer. Alternatively, create the spreadsheet in a Shared Drive where the service account has access.
Validation Errors
| Error Code | Cause |
VAL_MISSING_TITLE | Title is empty or whitespace. |
VAL_TITLE_TOO_LONG | Title exceeds 255 characters. |
VAL_INVALID_LOCALE | Locale is not a recognized BCP 47 locale string. |
VAL_DUPLICATE_SHEET_TITLE | The Sheets list contains two entries with the same Title — tab names must be unique within a spreadsheet. |
VAL_INVALID_CREDENTIAL | CredentialId does not resolve to a valid Google credential. |
QUOTA_EXCEEDED | The Google Drive storage quota for the account is exceeded. A new spreadsheet file cannot be created. |
Output Fields
| Field | Type | Description |
spreadsheetId | string | The unique ID of the newly created spreadsheet. Use this in all subsequent sheet operations targeting this file. |
spreadsheetUrl | string | The full Google Sheets URL for the new spreadsheet, e.g. https://docs.google.com/spreadsheets/d/{id}/edit. Suitable for sharing in emails or Slack notifications. |
title | string | The title of the created spreadsheet — echoed from the request. |
sheetsCreated | integer | Number of sheet tabs created. Equal to the length of the Sheets list, or 1 if no Sheets were specified (the default "Sheet1" tab). |
status | string | "success". |
resource | string | Always "spreadsheet". |
operation | string | Always "create". |
Sample Configuration
{
"resource": "spreadsheet",
"operation": "create",
"CredentialId": 12,
"Title": "{{ $json.clientName }} — Analytics {{ $now | date('YYYY') }}",
"Sheets": [
{ "Title": "Dashboard", "Hidden": false },
{ "Title": "Monthly KPIs","Hidden": false },
{ "Title": "Raw Data", "Hidden": false },
{ "Title": "Config", "Hidden": true }
],
"Locale": "en_US",
"RecalculationInterval": "ON_CHANGE"
}
Sample Output
{
"spreadsheetId": "1Xt4QzpNnFMdKvBdBZjgmRk9VE2upms7qptlbs74OgVE",
"spreadsheetUrl": "https://docs.google.com/spreadsheets/d/1Xt4QzpNnFMdKvBdBZjgmRk9VE2upms7qptlbs74OgVE/edit",
"title": "Acme Corp — Analytics 2026",
"sheetsCreated": 4,
"status": "success",
"resource": "spreadsheet",
"operation": "create"
}
Expression Reference
Assuming this node is named createClientWorkbook in the workflow:
| Expression | Returns | Description |
{{ $output.createClientWorkbook.spreadsheetId }} | string | New spreadsheet ID — pass to all subsequent sheet operations. |
{{ $output.createClientWorkbook.spreadsheetUrl }} | string | Full URL — embed in email notifications or Slack messages. |
{{ $output.createClientWorkbook.title }} | string | Spreadsheet title — confirm the created name in audit log. |
{{ $output.createClientWorkbook.sheetsCreated }} | integer | Number of tabs created in the new spreadsheet. |
{{ $output.createClientWorkbook.status }} | string | "success" on success port. |
Node Policies & GuardRails
- Ownership and sharing: Spreadsheets created by a service account are owned by that service account's Google Drive. Human users cannot see them unless explicitly shared. After creation, use an
HttpRequest node to call POST https://www.googleapis.com/drive/v3/files/{id}/permissions with the target user's email and role (writer or reader).
- Idempotency:
spreadsheet/create always creates a new file — it does not check for existing spreadsheets with the same title. Google Drive allows multiple files with identical names. In automated workflows, store the returned spreadsheetId in a database or Variable node to prevent duplicate creation on retry.
- Sheet definition efficiency: Defining all initial tabs in the
Sheets parameter is more efficient than creating the spreadsheet with one tab and then calling sheet/create multiple times. All tabs are created in a single API call.
- Drive quota: Each spreadsheet consumes Google Drive storage quota. Service accounts have 15 GB of free storage. Monitor storage usage in multi-tenant or high-volume workflows that create many spreadsheets.
- Locale impact: The
Locale setting affects how date and number functions behave in the spreadsheet. Set it explicitly when creating spreadsheets for users in specific regions to ensure consistent formula behavior — particularly for date parsing functions like DATEVALUE().
- Credential scope: The OAuth credential or service account must have the
https://www.googleapis.com/auth/spreadsheets and https://www.googleapis.com/auth/drive scopes authorized to create files in Google Drive.
Workflow Examples
Example 1 — New Client Onboarding — Full Workbook Provisioning
// Trigger: Webhook — new client signed contract in CRM
// Node 1 — spreadsheet/create (createWorkbook)
{
"resource": "spreadsheet", "operation": "create",
"CredentialId": 12,
"Title": "{{ $json.clientName }} — BizFirst Analytics {{ $now | date('YYYY') }}",
"Sheets": [
{ "Title": "Dashboard" },
{ "Title": "Monthly KPIs" },
{ "Title": "Weekly Data" },
{ "Title": "Config", "Hidden": true }
],
"Locale": "{{ $json.locale }}"
}
// Node 2 — HttpRequest: share with client's admin email
// POST https://www.googleapis.com/drive/v3/files/{{ $output.createWorkbook.spreadsheetId }}/permissions
// Body: { "role": "writer", "type": "user", "emailAddress": "{{ $json.clientAdminEmail }}" }
// Node 3 — HttpRequest (CRM): update client record with spreadsheetId and spreadsheetUrl
// Node 4 — Email to client: "Your analytics workbook is ready: {{ $output.createWorkbook.spreadsheetUrl }}"
Example 2 — Annual Fiscal Year Report Setup
// Trigger: Scheduled — January 1st at 1 AM
{
"resource": "spreadsheet", "operation": "create",
"CredentialId": 14,
"Title": "BizFirst Annual Report {{ $now | date('YYYY') }}",
"Sheets": [
{ "Title": "Jan {{ $now | date('YYYY') }}" },
{ "Title": "Feb {{ $now | date('YYYY') }}" },
{ "Title": "Mar {{ $now | date('YYYY') }}" },
{ "Title": "Apr {{ $now | date('YYYY') }}" },
{ "Title": "May {{ $now | date('YYYY') }}" },
{ "Title": "Jun {{ $now | date('YYYY') }}" },
{ "Title": "Jul {{ $now | date('YYYY') }}" },
{ "Title": "Aug {{ $now | date('YYYY') }}" },
{ "Title": "Sep {{ $now | date('YYYY') }}" },
{ "Title": "Oct {{ $now | date('YYYY') }}" },
{ "Title": "Nov {{ $now | date('YYYY') }}" },
{ "Title": "Dec {{ $now | date('YYYY') }}" },
{ "Title": "Full Year Summary" }
],
"Locale": "en_US"
}
// Store spreadsheetId in database for use by all monthly reporting workflows