Skip to content
Data

Tables & data

Tables are built-in spreadsheets your workflows and agents read and write, with no external database to set up. A workflow can find rows, insert and update them, delete them, or start when a row changes.

How tables are stored

A table's rows live in one shared store, with your columns packed into a single data field per row (there is no external database to connect or configure). Every row also carries three system fields you don't define yourself:

System fields on every row
System fieldMeaning
idThe row’s primary key. Matches the reserved filter name id (see filtering below).
priorityAn integer used for the default sort order.
created_atTimestamp set when the row was inserted.

These fields come back alongside your own columns on every read. A few names are reserved and can't be used for your own columns: id, data_source_id, tenant_id, data, priority, row_index, created_at, and updated_at. A write to one of them is refused.

The CRUD operations

There are five underlying operations. The workflow builder and the agent-facing table tool label them slightly differently, but they map onto the same behavior:

Table operations in the builder and the agent tool
Builder nodeAgent tool actionWhat it does
Find Rowsquery_rowsQuery rows by a filter and return them as an items list, meant to feed a Split step for per-row parallel processing. Find Rows does not split by itself.
Get Rowquery_rowsQuery rows by a filter and return them as a flat rows list. Find Rows and Get Row are separate builder nodes, but both resolve to the same underlying read.
Create Rowinsert_rowsAdd a new row from a map of column values.
Update Rowupdate_rowsChange columns on every row matching a filter.
Delete Rowdelete_rowsRemove every row matching a filter.
Create Columnadd_columnsAdd a column to the table, optionally backfilling existing rows with a default value.

The agent table tool also exposes table-level actions (create, get, list, update, delete) and marketplace actions (publish, unpublish), plus help.

Column types

A column is one of fifteen types, which control how it's validated and shown:

Column types
TypeNotes
textFree text.
numberNumeric value (stored, but compared as text: see filtering below).
dateDate value.
checkboxBoolean.
selectOne choice from a fixed list. Requires a non-empty options list at creation.
multi_selectSeveral choices from a fixed list. Also requires a non-empty options list.
ratingNumeric rating.
sentimentSentiment value.
progressProgress value.
fileA stored file, shown as a card. See file and image columns below.
imageThe same value as file, shown as a thumbnail.
emailEmail address.
phonePhone number.
urlURL.
vectorEmbedding vector for similarity search. Requires a dimension (1 to 2000). Plan-gated on the managed cloud: see below.

Values are converted to the column type

Every value you write is converted to its column's type: a number column turns "3,14" or "42%" into a number, a checkbox accepts yes or 1, and so on. A value that doesn't fit its column does not fail the write: instead the write reports warnings. Most are harmless normalizations, but a value that can't be parsed leaves the cell empty, and a file reference with nothing to fetch it by is stored but unusable.

In a workflow, read them from the step output as {{table:<label>.output.warnings}}; the agent table tool adds them to its reply. A vector value is the exception: a malformed embedding fails the whole write and no row is stored.

File and image columns

A file column and an image column hold the same value, a file reference, and differ only in how the grid shows it (a card or a thumbnail). In the grid, a cell can take an upload, an existing file picked from Files, or an external URL. Reads return the file reference as an object (id, path, mimeType, and size appear only when known):

File or image cell value
{
  "_type": "file",
  "id": "b21f6c1e-5a9d-4c1b-9d0e-2f7a8c3e41d2",
  "url": "/api/proxy/files/by-id/b21f6c1e-5a9d-4c1b-9d0e-2f7a8c3e41d2/raw",
  "name": "report.pdf",
  "mimeType": "application/pdf",
  "size": 48213
}

Write a whole file reference (for example {{core:dl.output.file}}) into the cell, not one of its fields. Store files in a file or image column, not a textcolumn: a text column keeps whatever text it was given and gives it back as text. A file cell can't be used as a filter value (it matches nothing).

Filtering: the rule that trips people up

A filter (where) is a bare column name, an operator, and a value: write status, not data.status (a data. prefix is stripped for you). The reserved name idmatches a row's primary key, not a column you defined.

Filter operators and how reliable they are
OperatorReliable?
=, !=, IN, IS NULL, IS NOT NULL, LIKEYes, use these.
>, <, >=, <=Textual order only: unreliable for numbers and dates.

LIKE does not add wildcards automatically: include % or _ in the value yourself (for example %gmail.com). IN requires a non-empty list of values. Aliases are accepted too: ==, EQ, NE, GT, LT, GTE, LTE, CONTAINS (same as LIKE, still without automatic wildcards), ISNULL, and NOTNULL.

Reading & pagination

How many rows a read returns, and how many it can return at most, depends on where it runs from:

Row limits by caller
CallerRows per read
Workflow Get Row nodeUp to 500 rows per read. With no limit set it reads up to 500; a higher limit is lowered to 500.
Workflow Find Rows nodeKeeps 100 rows by default. You can set a higher limit, but the read behind it still returns at most 500 rows.
Agent table tool (query_rows)20 rows by default, up to 10,000. There is no offset: to page through a large table, narrow the where filter instead of raising the limit.
The table grid in the app20 rows per page by default; choose 10, 20, 50, or 100 per page.

Every returned row carries the system fields id, priority, and created_at alongside your own columns. The default sort is priority DESC, id DESC (newest or highest priority first). There is no user-configurable sort on Find Rows or Get Row, so sort downstream in a Code step if you need a specific order. A limit of 0 returns no rows, which makes a cheap existence check.

Writing rows

Insert takes a map of column to value. Update takes a filter plus a non-empty set map: both are required, or the write fails fast. An empty value is stored as an empty string (not a true null).

text
Insert "Save contact":
  columns = { name: "{{trigger:form.output.name}}", email: "{{trigger:form.output.email}}" }

Update "Mark done":
  where = { column: "status", operator: "=", value: "in_progress" }
  set   = { status: "completed" }

Deleting

Delete always needs a wherefilter: there is no “clear table” operation. To wipe every row, match on the always-present primary key:

text
Delete "Clear table":
  where = { column: "id", operator: "IS NOT NULL" }

Don't create duplicates

When a workflow can run more than once on the same item, guard your insert so re-runs don't pile up duplicate rows:

  1. Find by a unique key
    Look the item up first with Find Rows, for example where: { column: "message_id", operator: "=", value: "{{trigger:gmail.output.id}}" }.
  2. Decide on the count
    A Decision on {{table:check.output.item_count}} == 0splits “new” from “already there”.
  3. Insert only on the new branch
    Insert on the new branch; end on the other. The write is now idempotent.

Creating a column

A select or multi_select column restricts values to a set of choices you provide: creation is rejected if that options list is empty. A vectorcolumn needs a declared dimension (1 to 2000) and a distance metric. When you add a column with a default value, existing rows that don't already have that key are back-filled with the default automatically.

Exporting a table

In the table grid, Export downloads the rows as CSV, JSON, or Excel, either the current view or all data. There is no CSV import button in the grid: to fill a table from a file, read the file with an Extract from File step and insert the rows, or ask the assistant to create the table with its data.

Vector similarity search

Tables store and search embeddings; they don't create them. Generate the embedding with an embeddings API (through an integration or an HTTP Request step) and insert it into the vector column. To prepare documents, an Extract from File step in text mode splits a PDF, Word, HTML, or text file into chunks you can embed one by one (see Files & storage).

A similarity search is a read with a similarity block instead of (or alongside) where: column, queryVector, an optional topK (default 5), and an optional threshold. The query vector must match the column's declared dimension. The distance metric is not part of the query: it is set on the vector column when you create it (cosine by default, or l2 or dot). Combine where with similarity for hybrid search: the filter narrows the candidate rows before nearest-neighbor ranking runs.

When a row changes

Every insert, update, and delete fires a row-changed event once it commits. This is what powers a datasource trigger: a workflow that starts automatically whenever a row in a chosen table is created, updated, or deleted. An update event also carries the row's previous values, so a trigger can compare before and after.

Troubleshooting

Common table problems
SymptomCauseFix
A > or < filter skips rows it should matchComparisons are textual, so "100" sorts before "9".Filter with = or IN on known values, or compare in a Code step.
A delete fails because it has no filterDelete always needs a where filter: there is no clear-table operation.To remove every row, filter on id with IS NOT NULL.
Re-running a workflow creates duplicate rowsThe insert runs every time, even for an item already stored.Look the item up with Find Rows first and insert only when item_count is 0.
A read returns fewer rows than the table holdsA workflow read returns at most 500 rows, and Find Rows keeps 100 by default.Narrow the filter, or page with offset on Get Row.
A stored file comes back as textIt was saved in a text column.Use a file or image column and map the whole file reference into it.
A write succeeds but a cell is emptyThe value could not be converted to the column type. The write reports it in warnings instead of failing.Read the warnings output and fix the value or the column type.

Related pages