Data Tables
Persistent tabular storage that workflows can read from and write to across multiple runs
Data Tables are a built-in database for workflows. Unlike node outputs (which only exist within a single run), data in a table persists indefinitely and is accessible by any workflow in your workspace.
Use Cases
- State tracking: remember which items have been processed, track run counts, store checkpoints
- Accumulating results: collect one row per execution across many runs, then query the accumulated data
- Cross-workflow communication: one workflow writes data, another reads it
- Simple lookups: store reference data (e.g. user preferences, config values) that workflows look up at runtime
- Audit logs: record every workflow action to a table for review or export
Creating a Data Table
- Open Data Tables in the Workflows section of the sidebar
- Click New Table
- Define your columns: name, type (text, number, boolean, date, JSON), and whether the column is required
- Save the table
Tables are workspace-wide and can be used by any workflow.
Reading Data
Use the Get Rows action (or Find First Row for a single result):
| Input | Description |
|---|---|
| Table | Select the data table |
| Filters | Optional conditions, e.g. status = "pending" |
| Sort By | Column to sort results by |
| Sort Direction | Ascending or descending |
| Limit | Maximum number of rows to return |
Get Rows outputs:
| Output | Type | Description |
|---|---|---|
rows | array | Matching rows as objects |
rowCount | number | Number of rows returned |
Find First Row outputs:
| Output | Type | Description |
|---|---|---|
row | object | The first matching row, or null if none found |
found | boolean | Whether a matching row was found |
Writing Data
Insert Row
Add a new row to the table.
| Input | Description |
|---|---|
| Table | Select the data table |
| Row Data | Key-value pairs mapping column names to values |
Outputs: rowId (the new row's ID), row (the full inserted row)
Upsert Row
Insert a new row, or update an existing one if a match is found. Useful for "create or update" patterns without needing a prior lookup.
| Input | Description |
|---|---|
| Table | Select the data table |
| Match Columns | Which columns to use as the unique key |
| Row Data | Values to set (for both insert and update) |
Update Rows
Update all rows matching a filter.
| Input | Description |
|---|---|
| Table | Select the data table |
| Filters | Conditions identifying which rows to update |
| Updates | Column values to set on matched rows |
Output: updatedCount, the number of rows modified
Delete Rows
Remove all rows matching a filter.
| Input | Description |
|---|---|
| Table | Select the data table |
| Filters | Conditions identifying which rows to delete |
Output: deletedCount, the number of rows removed
Count Rows
Count how many rows match a filter without returning the rows themselves. Useful for checking whether a table is empty or measuring progress.
Output: count, the number of matching rows
Viewing and Editing Data
Open any data table from Data Tables in the sidebar to view and manually edit its contents. You can:
- Browse, filter, and sort rows
- Manually add or delete rows
- Edit individual cell values inline
- Export the table to CSV
Best Practices
Use filters precisely. Get Rows without filters returns all rows. Add a Limit to avoid loading unexpectedly large datasets into a workflow node.
Use Upsert for idempotent writes. If a workflow might run multiple times for the same input (e.g. re-triggered by a webhook retry), use Upsert instead of Insert to avoid duplicate rows.
Clean up old data. Periodically delete processed rows with a scheduled workflow that deletes rows older than a threshold date, to keep tables from growing unboundedly.
JSON columns for flexible data. If the shape of a row's data varies (e.g. raw API payloads), use a JSON column rather than creating many optional text columns.