Use the Table block to read and write structured data in Sim. Data persists across runs and is scoped to the workspace. Operations insert, update, upsert, query, or delete rows, with filters, sorting, and batch writes.
Create tables
Tables are created from the Tables section in the sidebar. Each table requires:
- Name: An ASCII letter or underscore followed by letters, digits, or underscores (e.g.,
customer_leads) - Description: Optional description of the table's purpose
- Schema: Define columns with name, type, and optional constraints
Column types
See Table column types for the supported types and their storage behavior.
Column constraints
- Required: Column must have a value (cannot be null)
- Unique: Values must be unique across all rows (enables upsert matching)
Use the block
Create and manage custom data tables. Store, query, and update structured data within workflows. Query Rows accepts a plain predicate — {"field":"wins","op":"gte","value":10} — for one condition. Use all (AND) or any (OR) groups for multiple or nested conditions. Operators: eq, ne, gt, gte, lt, lte, in, nin, like, ilike, nlike, nilike, contains, ncontains, startsWith, endsWith, isNull, isNotNull, isEmpty, isNotEmpty. Order is a sort spec [{"field":"wins","direction":"desc"}]. Query Rows returns every matching row when Limit is omitted (fails if the result exceeds the fixed 5 MiB row-data budget — add a filter or a Limit). With a Limit, responses page: a non-null nextCursor means more rows exist — pass it back as the cursor. Columns to Return narrows each row to the selected columns (by stable id or name; one that no longer exists is skipped); leave it empty for every column.
Actions
Insert Row
Insert a new row into a table. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the row contents.
Inputs and outputs
Batch Insert Rows
Insert multiple rows into a table at once (up to 1000 rows)
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
rows | array | Yes | Array of row data objects (max 1000 rows) |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether rows were inserted |
rows | array | Inserted rows data |
insertedCount | number | Number of rows inserted |
message | string | Status message |
Upsert Row
Insert or update a row based on unique column constraints. If a row with matching unique field exists, update it; otherwise insert a new row. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the row contents.
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
data | object | Yes | Row data to insert or update |
conflictTarget | string | No | Unique column to match on. Required only when the table has more than one unique column. |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether row was upserted |
row | json | Upserted row data |
operation | string | Operation performed: insert or update |
message | string | Status message |
Update Row
Update an existing row in a table. Supports partial updates - only include the fields you want to change. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the fields to update.
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
rowId | string | Yes | Row ID to update |
data | object | Yes | Updated row data |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether row was updated |
row | json | Updated row data |
message | string | Status message |
Update Rows by Filter
Update multiple rows that match filter criteria. Data is merged with existing row data.
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
filter | object | Yes | Filter criteria using operators like $eq, $ne, $gt, $lt, $contains, $ncontains, $startsWith, $endsWith, $in, $nin, $empty, etc. |
data | object | Yes | Fields to update (merged with existing data) |
limit | number | No | Maximum number of rows to update (default: no limit, max: 1000) |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether rows were updated |
updatedCount | number | Number of rows updated |
updatedRowIds | array | IDs of updated rows |
message | string | Status message |
Delete Row
Delete a row from a table
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
rowId | string | Yes | Row ID to delete |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether row was deleted |
deletedCount | number | Number of rows deleted |
message | string | Status message |
Delete Rows by Filter
Delete multiple rows that match filter criteria. Use with caution - supports optional limit for safety.
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
filter | object | Yes | Filter criteria using operators like $eq, $ne, $gt, $lt, $contains, $ncontains, $startsWith, $endsWith, $in, $nin, $empty, etc. |
limit | number | No | Maximum number of rows to delete (default: no limit, max: 1000) |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether rows were deleted |
deletedCount | number | Number of rows deleted |
deletedRowIds | array | IDs of deleted rows |
message | string | Status message |
Query Rows
Query rows with a typed predicate filter and cursor pagination. A single filter can be a plain condition: \{"field":"wins","op":"gte","value":10\}. Use all (AND) or any (OR) groups for multiple or nested conditions. Operators: eq, ne, gt, gte, lt, lte, in, nin, like, ilike, nlike, nilike, contains, ncontains, startsWith, endsWith, isNull, isNotNull, isEmpty, isNotEmpty. Order is a sort spec, e.g. [\{"field":"wins","direction":"desc"\}]. Omit limit to return the entire result — the query fails if it exceeds the fixed 5 MiB row-data budget (narrow with a filter or set a limit). With a limit, a page can end early at the byte budget: a non-null nextCursor means more rows exist — pass it back as cursor to continue; never infer completion from page size.
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
filter | json | No | Predicate condition, e.g. \{"field":"wins","op":"gte","value":10\}. Use all or any for multiple conditions; omit to match all rows. |
columns | array | No | Stable column IDs or table column names to include in each row data object. Omit or pass an empty array to return all columns. A reference that matches no column is ignored. |
order | json | No | Sort spec, e.g. \[\{"field":"wins","direction":"desc"\}\]. |
limit | number | No | Maximum rows per page. Omit to return the entire matching result — fails if it exceeds the fixed 5 MiB row-data budget. With a limit, pages may byte-cut early and set nextCursor when more remain. |
cursor | string | No | Opaque pagination cursor returned by a prior query. Omit for the first page. |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether the query succeeded |
rows | array | Query result rows |
rowCount | number | Number of rows returned |
totalCount | number | Total rows matching the predicate (computed on the first page only) |
limit | number | Limit used in the query |
nextCursor | string | Cursor to fetch the next page, or null on the last page |
Get Row
Get a single row by ID
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
rowId | string | Yes | Row ID to retrieve |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether row was retrieved |
row | json | Row data |
message | string | Status message |
Get Schema
Get the schema configuration of a table
Inputs and outputs
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
tableId | string | Yes | Table ID in the execution workspace. In a workflow, the table must belong to the same workspace as the workflow. |
Output
| Parameter | Type | Description |
|---|---|---|
success | boolean | Whether schema was retrieved |
name | string | Table name |
columns | array | Column definitions (each includes its stable id) |
columnCount | number | Number of columns |
rowCount | number | Number of rows in the table |
maxRows | number | Max rows per table for the workspace's plan |
message | string | Status message |
Filter operators
A filter is a predicate. One condition is an object naming a column, an operator, and a value:
{"field": "status", "op": "eq", "value": "active"}| Operator | Description | Example |
|---|---|---|
eq | Equals | {"field": "status", "op": "eq", "value": "active"} |
ne | Not equals | {"field": "status", "op": "ne", "value": "deleted"} |
gt | Greater than | {"field": "age", "op": "gt", "value": 18} |
gte | Greater than or equal | {"field": "score", "op": "gte", "value": 80} |
lt | Less than | {"field": "price", "op": "lt", "value": 100} |
lte | Less than or equal | {"field": "quantity", "op": "lte", "value": 10} |
in | In array | {"field": "status", "op": "in", "value": ["active", "pending"]} |
nin | Not in array | {"field": "type", "op": "nin", "value": ["spam", "blocked"]} |
contains / ncontains | Contains, or does not contain (case-insensitive) | {"field": "email", "op": "contains", "value": "@gmail.com"} |
like / nlike | Pattern match, * wildcard (case-sensitive) | {"field": "name", "op": "like", "value": "Dr.*"} |
ilike / nilike | Pattern match, * wildcard (case-insensitive) | {"field": "name", "op": "ilike", "value": "*jo*"} |
startsWith | Starts with (case-insensitive) | {"field": "name", "op": "startsWith", "value": "Dr."} |
endsWith | Ends with (case-insensitive) | {"field": "file", "op": "endsWith", "value": ".pdf"} |
isNull / isNotNull | Cell is (not) null | {"field": "phone", "op": "isNull"} |
isEmpty / isNotEmpty | Cell is (not) empty | {"field": "phone", "op": "isEmpty"} |
Most columns are scalar (string, number, boolean, date) or opaque JSON; use ilike with *value* for substring matching on text.
Select columns accept only a subset of these operators, and a query using any other operator on one is rejected rather than returning no rows:
| Column | Allowed operators |
|---|---|
| Single-select | eq, ne, in, nin, isEmpty, isNotEmpty, isNull, isNotNull |
| Multi-select | contains, ncontains, isEmpty, isNotEmpty, isNull, isNotNull |
A multi-select cell holds a list of options, so match it with contains (by option name) rather than ilike.
Combine filters
Wrap conditions in all for AND:
{
"all": [
{"field": "status", "op": "eq", "value": "active"},
{"field": "age", "op": "gte", "value": 18}
]
}Use any for OR:
{
"any": [
{"field": "status", "op": "eq", "value": "active"},
{"field": "status", "op": "eq", "value": "pending"}
]
}Groups nest, so mixed logic is a group inside a group:
{
"all": [
{"field": "status", "op": "eq", "value": "active"},
{"any": [
{"field": "plan", "op": "eq", "value": "pro"},
{"field": "score", "op": "gte", "value": 90}
]}
]
}Omit the filter entirely to match every row.
Sort specification
Order is a list of column/direction pairs, applied in order:
[{"field": "createdAt", "direction": "desc"}]Multi-column sorting:
[
{"field": "priority", "direction": "desc"},
{"field": "name", "direction": "asc"}
]Pagination
Omit Limit to return every matching row in one response; queries exceeding the fixed 5 MiB row-data budget fail. Narrow with a filter, select fewer columns, or set a limit.
With a Limit, results page. A page can end at the limit or early at the row-data budget (5 MiB by default; self-hosted operators can configure TABLE_MAX_PAGE_BYTES), so a short page does not mean the end. Pass the returned nextCursor back as Cursor to fetch the next page and stop only when nextCursor is null — never infer completion from the row count.
Built-in columns
Every row automatically includes:
| Column | Type | Description |
|---|---|---|
id | string | Unique row identifier |
createdAt | date | When the row was created |
updatedAt | date | When the row was last modified |
These can be used in filters and sorting.
Limits
Workspace access
- Tables are scoped to workspaces and accessible from any workflow within that workspace
- Data persists across workflow executions
- Use unique constraints to enable upsert functionality
- Use the visual filter and sort builders to construct queries without writing JSON
