Data Table block
Configure the Data Table block to query, insert, update and delete rows in a Loopfour table from a workflow, with the output of each one.
The Data Table block (type: "data_table") reads and writes rows in Loopfour tables, and creates and organises tables and table folders. The operation field picks what it does. For the plain-language guide, see Tables: keep working data in Loopfour.
When to use it
- Keep a working list across runs, such as invoices already sent or payments already matched, and check it before acting.
- Store rows from an API, CSV or report for review in Tables or a dashboard.
- Create one table per period with
ensureTable, so each run writes to the right place.
Operations
operation | Editor label | What it does | Fields it needs |
|---|---|---|---|
insert | Insert | Adds one row. | tableId, data |
update | Update | Changes the rows where matchKey equals matchValue. | tableId, matchKey, matchValue, data |
upsert | Upsert | Updates the matching row, or inserts one if none matches. | tableId, matchKey, matchValue, data |
upsertMany | Upsert many | Upserts a list of rows in one transaction, matched on matchKey. | tableId, matchKey, rows |
delete | Delete | Deletes the rows where matchKey equals matchValue. | tableId, matchKey, matchValue |
find | Find | Returns rows that match filter, newest first. | tableId |
getById | Get by ID | Returns one row by its id. | tableId, rowId |
createTable | Create table | Gets or creates a table by name. | name, columns |
ensureTable | Ensure table | Gets or creates a table by name in a folder, and adds any missing columns. | name, columns, folderId |
createTableInFolder | Create table in folder | Gets or creates a table in a folder. | name, columns, folderId |
listTables | List tables | Lists tables, optionally in one folder. | None |
listFolders | List folders | Lists table folders. | None |
ensureFolder | Ensure folder | Gets or creates a folder by name. | folderName |
assignTableToFolder | Assign table to folder | Moves a table to a folder, or to the root. | tableId, folderId |
Configuration
Field (config key) | Editor label | Type | Meaning |
|---|---|---|---|
operation | Operation | string | One of the operations above. Required. |
tableId | Table | string | The table's id. The editor lists your tables. |
data | Row Data | object | The row, keyed by column name, such as { "invoiceNumber": "INV-1042", "status": "sent" }. A whole template reference that resolves to an object also works. |
rows | Rows | array | For upsertMany: up to 2000 row objects, each with the matchKey field. Duplicate match values keep the last one. |
matchKey | Match Key | string | The column name to match on. |
matchValue | Match Value | any | The value to match, often a template reference. |
updateFilter | Update only when (JSON) | object | For update only. Each column must hold one of the listed values, checked in the same write: { "status": ["", "failed"] }. "" also matches an empty cell. |
filter | Filter | object | For find: column name to value. Every pair must match exactly. Operator objects such as $gt are not supported yet and fail the step. |
limit | Limit | number | For find: 1 to 1000, default 50. |
offset | Offset | number | For find: rows to skip, default 0. |
rowId | Row ID | string | For getById. |
name | Table Name | string | For the create and ensure operations. Up to 255 characters. Accepts template references, such as AR aging {{input.period}}. |
description | Table Description | string | Optional description for a new table. |
columns | Columns | array | For the create and ensure operations: up to 100 { "name", "type" } entries. type is text (default), number, boolean, date, select, url, email or json. Names must be unique, ignoring case. |
maxRows | Max Rows | number | Row limit for a new table: 1 to 100000, default 10000. |
folderId | Folder ID | string or null | The table folder. null (or the text null in the editor) means the root. For listTables, omit it to list every table. |
folderName | Folder Name | string | For ensureFolder: 1 to 255 characters. |
ensureTable adds columns that the table lacks. It does not change or remove a column, and it fails if a column with the same name has a different type, letter case or surrounding spaces.
Output
The output depends on the operation:
| Operation | Fields |
|---|---|
insert, getById, upsert (when it inserted) | The row: id, tableId, data, fields, createdAt, updatedAt. |
update, upsert (when it matched) | updated: rows changed. |
upsertMany | total, updated, inserted. |
delete | deleted: rows removed. |
find | rows (each with id, data and fields) and pagination: limit, offset, returned, nextOffset, hasMore. |
createTable, ensureTable, createTableInFolder | tableId, name, folderId, created, columns; ensureTable also returns addedColumns. |
listTables | tables, count. |
listFolders | folders, count. |
ensureFolder | folderId, folder, created. |
assignTableToFolder | tableId, folderId, previousFolderId, changed. |
A row's data is keyed by internal column id (col_…). Read cells by column name from fields: {{steps.find_invoice.rows[0].fields.status}}. When a table has two columns with the same name, those names are left out of fields and listed in ambiguousFieldNames.
The editor's output list also shows result and success; the executor does not return those fields.
Example
This step records that an invoice reminder was sent, and only if it was not already sent or failed before.
{
"id": "mark_reminded",
"type": "data_table",
"name": "Mark reminder sent",
"config": {
"operation": "update",
"tableId": "8c1f0f5e-2a4b-4c55-9a64-0d2f3e6b7a10",
"matchKey": "invoiceNumber",
"matchValue": "{{variables.invoice.number}}",
"updateFilter": { "reminderStatus": ["", "failed"] },
"data": { "reminderStatus": "sent", "reminderRunId": "{{run.id}}" }
}
}If updated is 0, another run already marked the row.
Errors and limits
- A config that misses a field its operation needs fails the step with
Invalid data_table config, naming the field. insertfails withData table row limit reachedwhen the table is atmaxRows.getByIdfails withRow not foundwhen no row has that id.datathat does not resolve to an object fails the step.updateanddeleteact on every row that matches. Use a column with unique values asmatchKey.- Most operations are also available as AI Agent tools, such as
data_table.find;upsertManyandcreateTableare not. See AI Agent block.
Loopfour