# Table (/integrations/table)





{/* MANUAL-CONTENT-START:intro */}

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 [#creating-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 [#column-types]

See [Table column types](/tables#column-types) for the supported types and their storage behavior.

### Column constraints [#column-constraints]

* **Required**: Column must have a value (cannot be null)
* **Unique**: Values must be unique across all rows (enables upsert matching)

{/* MANUAL-CONTENT-END */}

## Use the block [#usage-instructions]

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 [#actions]

### Insert Row [#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.

<ReferenceDetails>
  #### Input [#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 as JSON object |

  #### Output [#output]

  | Parameter | Type | Description |
  | - | - | - |
  | `success` | boolean | Whether row was inserted |
  | `row` | json | Inserted row data |
  | `message` | string | Status message |
</ReferenceDetails>

### Batch Insert Rows [#batch-insert-rows]

Insert multiple rows into a table at once (up to 1000 rows)

<ReferenceDetails>
  #### Input [#input-1]

  | 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 [#output-1]

  | Parameter | Type | Description |
  | - | - | - |
  | `success` | boolean | Whether rows were inserted |
  | `rows` | array | Inserted rows data |
  | `insertedCount` | number | Number of rows inserted |
  | `message` | string | Status message |
</ReferenceDetails>

### Upsert Row [#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.

<ReferenceDetails>
  #### Input [#input-2]

  | 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 [#output-2]

  | 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 |
</ReferenceDetails>

### Update Row [#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.

<ReferenceDetails>
  #### Input [#input-3]

  | 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 [#output-3]

  | Parameter | Type | Description |
  | - | - | - |
  | `success` | boolean | Whether row was updated |
  | `row` | json | Updated row data |
  | `message` | string | Status message |
</ReferenceDetails>

### Update Rows by Filter [#update-rows-by-filter]

Update multiple rows that match filter criteria. Data is merged with existing row data.

<ReferenceDetails>
  #### Input [#input-4]

  | 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 [#output-4]

  | 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 |
</ReferenceDetails>

### Delete Row [#delete-row]

Delete a row from a table

<ReferenceDetails>
  #### Input [#input-5]

  | 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 [#output-5]

  | Parameter | Type | Description |
  | - | - | - |
  | `success` | boolean | Whether row was deleted |
  | `deletedCount` | number | Number of rows deleted |
  | `message` | string | Status message |
</ReferenceDetails>

### Delete Rows by Filter [#delete-rows-by-filter]

Delete multiple rows that match filter criteria. Use with caution - supports optional limit for safety.

<ReferenceDetails>
  #### Input [#input-6]

  | 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 [#output-6]

  | 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 |
</ReferenceDetails>

### Query Rows [#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.

<ReferenceDetails>
  #### Input [#input-7]

  | 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 [#output-7]

  | 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 |
</ReferenceDetails>

### Get Row [#get-row]

Get a single row by ID

<ReferenceDetails>
  #### Input [#input-8]

  | 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 [#output-8]

  | Parameter | Type | Description |
  | - | - | - |
  | `success` | boolean | Whether row was retrieved |
  | `row` | json | Row data |
  | `message` | string | Status message |
</ReferenceDetails>

### Get Schema [#get-schema]

Get the schema configuration of a table

<ReferenceDetails>
  #### Input [#input-9]

  | 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 [#output-9]

  | 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 |
</ReferenceDetails>

{/* MANUAL-CONTENT-START:notes */}

## Filter operators [#filter-operators]

A filter is a predicate. One condition is an object naming a column, an operator, and a value:

```json
{"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 [#combining-filters]

Wrap conditions in `all` for AND:

```json
{
  "all": [
    {"field": "status", "op": "eq", "value": "active"},
    {"field": "age", "op": "gte", "value": 18}
  ]
}
```

Use `any` for OR:

```json
{
  "any": [
    {"field": "status", "op": "eq", "value": "active"},
    {"field": "status", "op": "eq", "value": "pending"}
  ]
}
```

Groups nest, so mixed logic is a group inside a group:

```json
{
  "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 [#sort-specification]

Order is a list of column/direction pairs, applied in order:

```json
[{"field": "createdAt", "direction": "desc"}]
```

Multi-column sorting:

```json
[
  {"field": "priority", "direction": "desc"},
  {"field": "name", "direction": "asc"}
]
```

## Pagination [#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 [#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 [#limits]

| Resource | Limit |
| - | - |
| Tables per workspace | Depends on workspace plan and deployment settings |
| Rows per table | Depends on workspace plan and deployment settings |
| Columns per table | 1,000 |
| Max row size | 400 KiB by default; configurable on self-hosted deployments |
| Text value size | Constrained by the row byte limit |
| Query limit | 1,000 rows |
| Batch insert size | 1,000 rows |
| Bulk update/delete | 1,000 rows |

## Workspace access [#notes]

* 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

{/* MANUAL-CONTENT-END */}
