Portal Community

When to Use

Configuration

FieldRequiredDescription
CredentialIdRequiredInteger ID referencing a stored Google credential (Service Account or OAuth2) in the BizFirst Credentials Manager.
SpreadsheetIdRequiredThe Google Sheets spreadsheet ID. Found in the spreadsheet URL: https://docs.google.com/spreadsheets/d/{SpreadsheetId}/edit.
SheetNameRequiredThe exact name of the tab/sheet within the spreadsheet. Case-sensitive. Spaces are preserved — "Employee Roster" is different from "EmployeeRoster".
FiltersOptionalList of SheetFilterInfo objects. Each filter has: Column (header name), Operator (Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, GreaterOrEqual, LessOrEqual, IsEmpty, IsNotEmpty), Value (comparison value as string). When omitted, all rows are returned.
CombineFiltersOptionalEnum: And | Or. Default And. When And, a row must satisfy all filters to be included. When Or, a row that satisfies any single filter is included.
HeaderRowOptionalInteger. Default 1. The row number (1-based) that contains column headers. Used to map values to named keys in the output objects.
FirstDataRowOptionalInteger. Default 2. The first row (1-based) containing actual data. Rows between HeaderRow and FirstDataRow are skipped (useful when there are merged title rows).
ValueRenderOptionalString. Default "UNFORMATTED_VALUE". Controls how the Sheets API renders cell values. Options: UNFORMATTED_VALUE (raw underlying value, no formatting), FORMATTED_VALUE (display string as shown in the spreadsheet including number/date formatting), FORMULA (cell formula text). Use UNFORMATTED_VALUE for numeric comparisons and FORMATTED_VALUE when the display string is what downstream nodes expect.
DateTimeRenderOptionalString. Default "SERIAL_NUMBER". How dates and times are returned. Options: SERIAL_NUMBER (Excel-style numeric serial, e.g. 46000), FORMATTED_STRING (formatted as displayed in the sheet, e.g. "2026-01-15").
ReturnFirstMatchOptionalBoolean. Default false. When true, the node returns only the first row that satisfies all filter conditions and stops scanning. Use when you expect exactly one record (e.g. lookup by unique key) to avoid unnecessary processing.

Validation Errors

Error CodeCause
VAL_MISSING_SPREADSHEET_IDSpreadsheetId is empty or whitespace.
VAL_MISSING_SHEET_NAMESheetName is empty or whitespace.
VAL_INVALID_HEADER_ROWHeaderRow is less than 1 or greater than FirstDataRow.
VAL_INVALID_FILTER_OPERATORA filter Operator value is not one of the supported enum members.
VAL_INVALID_CREDENTIALCredentialId does not resolve to a valid Google credential in Credentials Manager.
SHEET_NOT_FOUNDThe specified SheetName does not exist in the spreadsheet. Check for typos and case sensitivity.

Output Fields

FieldTypeDescription
rowsarrayArray of row objects. Each object has one key per column header, with the cell value as the property value. Empty cells are included as empty strings.
rowCountintegerTotal number of rows returned after applying filters.
spreadsheetIdstringThe spreadsheet ID from the request, echoed back for chaining.
sheetNamestringThe sheet/tab name from the request, echoed back for chaining.
statusstring"success" on the success port.
resourcestringAlways "sheet".
operationstringAlways "get".

Sample Configuration

{
  "resource": "sheet",
  "operation": "get",
  "CredentialId": 12,
  "SpreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "SheetName": "Employees",
  "Filters": [
    { "Column": "Department", "Operator": "Equals",        "Value": "Engineering" },
    { "Column": "Status",     "Operator": "Equals",        "Value": "Active" },
    { "Column": "Salary",     "Operator": "GreaterOrEqual","Value": "60000" }
  ],
  "CombineFilters": "And",
  "HeaderRow": 1,
  "FirstDataRow": 2,
  "ValueRender": "UNFORMATTED_VALUE",
  "DateTimeRender": "FORMATTED_STRING",
  "ReturnFirstMatch": false
}

Sample Output

{
  "rows": [
    {
      "EmployeeId": "E-1042",
      "FirstName": "Sarah",
      "LastName": "Mitchell",
      "Department": "Engineering",
      "Title": "Senior Software Engineer",
      "Salary": 95000,
      "Status": "Active",
      "StartDate": "2023-03-14",
      "ManagerEmail": "james.carter@acme.com"
    },
    {
      "EmployeeId": "E-1087",
      "FirstName": "David",
      "LastName": "Okonkwo",
      "Department": "Engineering",
      "Title": "DevOps Engineer",
      "Salary": 88000,
      "Status": "Active",
      "StartDate": "2024-07-01",
      "ManagerEmail": "james.carter@acme.com"
    },
    {
      "EmployeeId": "E-1103",
      "FirstName": "Priya",
      "LastName": "Sharma",
      "Department": "Engineering",
      "Title": "Staff Engineer",
      "Salary": 120000,
      "Status": "Active",
      "StartDate": "2021-11-08",
      "ManagerEmail": "james.carter@acme.com"
    }
  ],
  "rowCount": 3,
  "spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "sheetName": "Employees",
  "status": "success",
  "resource": "sheet",
  "operation": "get"
}

Expression Reference

Assuming this node is named getEmployees in the workflow:

ExpressionReturnsDescription
{{ $output.getEmployees.rows }}arrayAll returned row objects as an array.
{{ $output.getEmployees.rows[0] }}objectFirst row object in the result set.
{{ $output.getEmployees.rows[0].Email }}stringEmail field of the first row (column header must be "Email").
{{ $output.getEmployees.rows[0].EmployeeId }}stringEmployeeId of the first row.
{{ $output.getEmployees.rowCount }}integerTotal number of rows matching the filters.
{{ $output.getEmployees.spreadsheetId }}stringSpreadsheet ID (useful for passing to subsequent write nodes).
{{ $output.getEmployees.sheetName }}stringSheet/tab name, echoed from the request.
{{ $output.getEmployees.rows | length }}integerAlternative row count via filter expression.
{{ $output.getEmployees.rows | map('Salary') | sum }}numberSum all Salary values across matching rows.

Node Policies & GuardRails

Workflow Examples

Example 1 — Weekly Payroll Summary Email

Trigger: Scheduled (every Friday 9 AM). Read all active employees from the Payroll sheet, compute total payroll cost, and send a summary email to Finance.

// Node 1 — sheet/get (getActiveEmployees)
{
  "resource": "sheet", "operation": "get",
  "CredentialId": 12,
  "SpreadsheetId": "{{ $env.PAYROLL_SHEET_ID }}",
  "SheetName": "Employees",
  "Filters": [{ "Column": "Status", "Operator": "Equals", "Value": "Active" }],
  "ValueRender": "UNFORMATTED_VALUE"
}

// Node 2 — Code node (computeTotals)
// $output.getActiveEmployees.rows  →  sum Salary field
// Outputs: totalPayroll, headcount

// Node 3 — Email node
{
  "To": "finance@acme.com",
  "Subject": "Weekly Payroll Summary — {{ $now | date('YYYY-MM-DD') }}",
  "Body": "Headcount: {{ $output.computeTotals.headcount }}\nTotal Payroll: ${{ $output.computeTotals.totalPayroll }}"
}

Example 2 — Inventory Reorder Alert

Trigger: Nightly at 2 AM. Read all inventory rows where stock is below the reorder threshold, then create purchase orders via the ERP HTTP API for each item.

// Node 1 — sheet/get (getLowStock)
{
  "resource": "sheet", "operation": "get",
  "CredentialId": 15,
  "SpreadsheetId": "{{ $env.INVENTORY_SHEET_ID }}",
  "SheetName": "Stock",
  "Filters": [
    { "Column": "CurrentQty", "Operator": "LessThan", "Value": "{{ $json.ReorderLevel }}" }
  ],
  "ValueRender": "UNFORMATTED_VALUE"
}

// Node 2 — Loop node over $output.getLowStock.rows
// Node 3 — HttpRequest: POST /erp/purchase-orders for each item

Example 3 — Single Product Lookup Before Quote

Trigger: Form submission with a SKU. Retrieve the product details and use them to populate a quote PDF template.

// Node 1 — sheet/get (getProduct)
{
  "resource": "sheet", "operation": "get",
  "CredentialId": 12,
  "SpreadsheetId": "{{ $env.CATALOG_SHEET_ID }}",
  "SheetName": "Products",
  "Filters": [{ "Column": "SKU", "Operator": "Equals", "Value": "{{ $json.sku }}" }],
  "ReturnFirstMatch": true
}

// Node 2 — PDF generation node
{
  "Template": "quote-template",
  "ProductName": "{{ $output.getProduct.rows[0].ProductName }}",
  "UnitPrice": "{{ $output.getProduct.rows[0].UnitPrice }}",
  "Description": "{{ $output.getProduct.rows[0].Description }}"
}