Google Sheets Action
Reads and writes a Google Sheets spreadsheet from a flow. Six operations — append a row, update one, append-or-update, read a range, read every row, or create a whole new spreadsheet — with payload fields mapped to columns by header name, so you build live dashboards, logs, and reports without code.
Overview
The Google Sheets Action node writes to (and reads from) a spreadsheet as part of a flow. It authenticates with a Google credential, and which fields the panel shows depends on the operation you pick. The node appears on the canvas with an emerald color.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Friendly label for the Sheets action |
| spreadsheetId | string | Yes | The Google Sheets spreadsheet ID (from the URL) |
| sheetName | string | Yes | Name of the sheet tab to write to (e.g., "Sheet1") |
| operation | string | Yes | One of the six values in Operations. Case matters — the API rejects anything else. |
| columnMapping | { header: string, valueTemplate: string }[] | Yes | Array of objects mapping header names (from row 1 of the sheet) to payload field templates. Each entry writes to the column that matches the given header. |
| credentials | object | Yes | Google service account JSON key. Encrypted at rest. |
| inputNodes | { nodeType, nodeId }[] | Yes | Upstream nodes that feed this one |
| isActive | boolean | No | Enable or disable without deleting. Default: true. |
spreadsheetId is the long string in the Google Sheets URL between /d/ and /edit. For example, in https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms/edit, the ID is 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms.Operations
The Operation dropdown decides what the node does and which fields the panel shows below it. Six values, and the API validates them exactly as written here:
| Value | In the dropdown | Does | Fields it shows |
|---|---|---|---|
| appendRow | Append Row | Adds a new row at the bottom | spreadsheet · sheet · columns |
| updateRow | Update Row | Updates the row whose match column equals your value | + match column · match value |
| appendOrUpdateRow | Append or Update | Updates if the row exists, appends if it does not | + match column · match value |
| readRange | Read Range | Reads the cells in a range you give | spreadsheet · sheet · range |
| getRows | Get Rows | Reads every row, using row 1 as the keys | spreadsheet · sheet |
| createSpreadsheet | Create Spreadsheet | Creates a new spreadsheet | name · save location |
append_row and update_cell. Neither is a valid value — the field is validated against the six above and anything else comes back as a 400. If you have a script built from the old text, that is why.Match column is picked from the spreadsheet's own headers, and match value is a template — typically {{payload.id}}. Together they are what makes appendOrUpdateRow idempotent: run the same flow twice and you get one row, not two.
createSpreadsheet is the only operation that asks for no existing document, and the only one with a Save location — the Drive folder the new file lands in.
Mode — action or AI toolkit
The Mode selector at the top of the panel decides who drives the node:
- Action — you pick the operation and fill the fields. The node does that one thing every time it runs.
- AI toolkit — the node stops being a fixed operation and becomes a set of tools an AI Node can call. The model chooses the operation and the arguments; the panel shows a preview of exactly which tools it will expose.
Create a Sheets Action
curl -X POST /api/sheets-actions \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Log Sheet",
"spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"sheetName": "Orders",
"operation": "appendRow",
"columnMapping": [
{ "header": "Order ID", "valueTemplate": "{{payload.order.id}}" },
{ "header": "Customer", "valueTemplate": "{{payload.customer.name}}" },
{ "header": "Email", "valueTemplate": "{{payload.customer.email}}" },
{ "header": "Total", "valueTemplate": "{{payload.order.total}}" },
{ "header": "Status", "valueTemplate": "{{response.status}}" },
{ "header": "Timestamp", "valueTemplate": "{{payload.timestamp}}" }
],
"credentials": {
"type": "service_account",
"project_id": "my-project-123",
"private_key_id": "key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "123456789",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
},
"inputNodes": [{ "nodeType": "webhook", "nodeId": "6642f1a2c3b4d5e6f7890123" }],
"isActive": true
}'Response
{
"_id": "6646d4e5f6a7b8c9d0123456",
"name": "Order Log Sheet",
"spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"sheetName": "Orders",
"operation": "appendRow",
"columnMapping": [
{ "header": "Order ID", "valueTemplate": "{{payload.order.id}}" },
{ "header": "Customer", "valueTemplate": "{{payload.customer.name}}" },
{ "header": "Email", "valueTemplate": "{{payload.customer.email}}" },
{ "header": "Total", "valueTemplate": "{{payload.order.total}}" },
{ "header": "Status", "valueTemplate": "{{response.status}}" },
{ "header": "Timestamp", "valueTemplate": "{{payload.timestamp}}" }
],
"inputNodes": [{ "nodeType": "webhook", "nodeId": "6642f1a2c3b4d5e6f7890123" }],
"isActive": true,
"organizationId": "6640a1b2c3d4e5f6a7890001",
"createdAt": "2025-05-17T10:00:00.000Z",
"updatedAt": "2025-05-17T10:00:00.000Z"
}credentials field (service account JSON) is encrypted before storage and never returned in API responses. Make sure the service account email has Editor access to the target spreadsheet.Update a Sheets Action
curl -X PATCH /api/sheets-actions/6646d4e5f6a7b8c9d0123456 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"sheetName": "Orders-2025",
"columnMapping": [
{ "header": "Order ID", "valueTemplate": "{{payload.order.id}}" },
{ "header": "Customer", "valueTemplate": "{{payload.customer.name}}" },
{ "header": "Email", "valueTemplate": "{{payload.customer.email}}" },
{ "header": "Total", "valueTemplate": "{{payload.order.total}}" },
{ "header": "Currency", "valueTemplate": "{{payload.order.currency}}" },
{ "header": "Status", "valueTemplate": "{{response.status}}" },
{ "header": "Processed At", "valueTemplate": "{{response.body.processedAt}}" },
{ "header": "Timestamp", "valueTemplate": "{{payload.timestamp}}" }
]
}'Response
{
"_id": "6646d4e5f6a7b8c9d0123456",
"name": "Order Log Sheet",
"spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
"sheetName": "Orders-2025",
"operation": "appendRow",
"columnMapping": [
{ "header": "Order ID", "valueTemplate": "{{payload.order.id}}" },
{ "header": "Customer", "valueTemplate": "{{payload.customer.name}}" },
{ "header": "Email", "valueTemplate": "{{payload.customer.email}}" },
{ "header": "Total", "valueTemplate": "{{payload.order.total}}" },
{ "header": "Currency", "valueTemplate": "{{payload.order.currency}}" },
{ "header": "Status", "valueTemplate": "{{response.status}}" },
{ "header": "Processed At", "valueTemplate": "{{response.body.processedAt}}" },
{ "header": "Timestamp", "valueTemplate": "{{payload.timestamp}}" }
],
"inputNodes": [{ "nodeType": "webhook", "nodeId": "6642f1a2c3b4d5e6f7890123" }],
"isActive": true,
"organizationId": "6640a1b2c3d4e5f6a7890001",
"createdAt": "2025-05-17T10:00:00.000Z",
"updatedAt": "2025-05-17T11:45:00.000Z"
}Canvas Integration
On the visual canvas, the Google Sheets Action node uses the type sheets-action and is rendered with an emerald color scheme. Connect it to any webhook node to write data to Google Sheets after deliveries.
The node displays the action name, the spreadsheet name (if available), and the operation type. Edges from webhooks flow into the Sheets action node.
header value in each mapping entry must exactly match the text in row 1. Column order in the sheet does not matter — the system finds the correct column by header name, so you can reorder or insert columns freely without breaking your mapping.Payload Examples
Incoming Webhook Payload
{
"type": "order.completed",
"timestamp": "2025-05-17T10:30:00Z",
"customer": {
"name": "Jane Doe",
"email": "[email protected]"
},
"order": {
"id": "ORD-9821",
"total": 149.99,
"currency": "USD"
}
}Resulting Spreadsheet Row
| Order ID | Customer | Total | Status | Timestamp | |
|---|---|---|---|---|---|
| ORD-9821 | Jane Doe | [email protected] | 149.99 | 200 | 2025-05-17T10:30:00Z |
Column Mapping Array
[
{ "header": "Order ID", "valueTemplate": "{{payload.order.id}}" },
{ "header": "Customer", "valueTemplate": "{{payload.customer.name}}" },
{ "header": "Email", "valueTemplate": "{{payload.customer.email}}" },
{ "header": "Total", "valueTemplate": "{{payload.order.total}}" },
{ "header": "Status", "valueTemplate": "{{response.status}}" },
{ "header": "Timestamp", "valueTemplate": "{{payload.timestamp}}" }
]Use Cases
Order Tracking Spreadsheet
Append a row for every completed order. Share the spreadsheet with your operations team for a live view of incoming orders without building a custom dashboard.
Webhook Delivery Log
Log every delivery result to a spreadsheet for auditing. Include the event type, delivery status, response body, and timestamp. Use triggerOn: always to capture both successes and failures.
Lead Capture Pipeline
Receive form submission webhooks and append lead information (name, email, company, message) to a shared Google Sheet that your sales team monitors.
Financial Reporting
Capture payment events with amounts, currencies, and customer details in a spreadsheet. Use Google Sheets formulas and charts to build real-time financial reports.
API Reference
| Method | Webhook | Description |
|---|---|---|
| GET | /api/sheets-actions | List all Sheets actions for your organization |
| POST | /api/sheets-actions | Create a new Sheets action |
| GET | /api/sheets-actions/:id | Get a single Sheets action by ID |
| PATCH | /api/sheets-actions/:id | Update a Sheets action |
| DELETE | /api/sheets-actions/:id | Delete a Sheets action |