Introduction
A Data Table is a typed table your workflows read and write — the register that makes a run remember.
A Data Table is a small typed table that belongs to a project. A workflow writes rows into it with a Data Table node, and people read and correct those rows on the Studio → Data Tables page.
It exists for the thing a single run cannot do: remember. Extract a contract today, and a schedule three weeks from now can still find its deadline, because the deadline is a row rather than a value inside a finished run.
A workflow is not the only thing that can use a table:
- an Agent can read it, and add or change rows — reads and each kind of write are switched on separately, per table, and an Agent can never delete;
- an Agent's skill scripts can read a whole table as a file, when that is switched on for the table;
- a workflow's Code node can read matching rows as a file.
A Package can ship a table and wire all of these to it; the authoring reference is Data Tables in the portable-definitions spec.
What a table is made of
| Part | What it is |
|---|---|
| Name | Unique inside the project, compared without regard to case. |
| Columns | Ordered, each with a name and one of four types. The order you declare is the column order. |
| Rows | One value per column, plus three system fields Elizon maintains. |
The four column types are boolean, date, number and string. There are no joins, formulas or
relations, and a column's type cannot be changed once rows exist — add a new column instead.
Every row also carries three system fields you never declare and cannot use as a column name:
| Field | Meaning |
|---|---|
id | The row's identity (a UUID). |
createdAt | When the row was first written. |
updatedAt | When it was last changed. |
You can sort and filter by them like any other field.
Values and empties
A cell holds a string, a number, a boolean, a date — or null, which is the single representation
of empty for every type. An object or an array is not a value: a write that puts one in a cell
is refused rather than stored as JSON, because no filter could compare it afterwards.
Values are checked on write, against the column's own type, and a refusal names the column, the type it expected and the value it got:
Column "Deadline" expects a date; received the string "next tuesday".
Accepted: an ISO 8601 date (2026-10-02) or date-time (2026-10-02T09:00:00Z), or null.| Type | Accepted on write |
|---|---|
boolean | true, false, "true", "false", or null |
date | An ISO 8601 date (2026-10-02) or date-time (2026-10-02T09:00:00Z) |
number | A finite number or a numeric string |
string | A string, a finite number, a boolean, or null/"" for empty |
A date is stored as a timestamp, so a value written as 2026-10-02 reads back as
2026-10-02T00:00:00.000Z.
Where a table lives
A Data Table belongs to one project — not to the organization, and not to a workflow. That is also the whole of its access control: a run in another project, or in another organization, cannot read or write it, by its name or by its id.
A brand-new organization has no projects at all, so create a project before you create a table.
Who may do what
Four permissions, independently grantable to a custom role:
| Permission | Allows | Owner | Admin | Editor | Normal |
|---|---|---|---|---|---|
data_tables.view | See the tables, their columns and their rows | ✔ | ✔ | ✔ | ✔ |
data_tables.create | Create a table | ✔ | ✔ | ✔ | — |
data_tables.edit | Rename a table; add, rename and remove columns; write rows | ✔ | ✔ | ✔ | — |
data_tables.delete | Drop a table, and everything in it | ✔ | ✔ | — | — |
A workflow run has no role. What a Data Table node may do is decided by the node's own configuration — which is why dropping a table from a workflow needs the node's explicit Allow deleting tables flag.
Limits
Every limit is a platform setting your operator can raise or lower; these are the defaults.
| Limit | Default |
|---|---|
| Tables per project | 50 |
| Columns per table | 50 |
| Rows per table | 100,000 |
| Bytes per row | 64 KB |
| Rows returned by one read | 1,000 |
| Statement timeout | 5 s |
| Conditions in one filter | 50 |
| Sort keys in one read | 8 |
| Rows in one write | 20,000 |
| Row source file size | 50 MB |
| Bulk statement timeout | 60 s |
KB and MB are binary multiples: 1 KB = 1,024 bytes, and 1 MB = 1,024 KB (1,048,576 bytes).
A write that would cross a limit is refused with the limit named, and a read that asks for more rows than the ceiling allows is clamped to it rather than refused — so "return all rows" means "up to the ceiling", never "unbounded". To read past the ceiling, page with a cursor, or read every matching row as a file.
Rows in one write bounds a bulk upsert and an insert, and Row source file size bounds the file a bulk write reads its rows from and the file a read as a file writes. The statement timeout bounds any query whose filters an author controls; the bulk statement timeout replaces it for a bulk upsert, an Optimize Bulk insert, a read as a file and a Studio import.
When a statement times out
Every Data Tables operation runs as one database transaction. When a statement in it runs past its timeout, the database cancels it and the whole transaction is rolled back: nothing the operation would have written is kept, and a read returns the error rather than partial rows.
A timeout is not one of the refusals that names its cause. Studio shows a generic internal error, a
Data Table node fails with the message
The Data Table operation could not be completed (internal error)., and an Agent's Data Table tool
call fails with a generic internal error. That the timeout fired is recorded only in the server
log, where your operator can see it.
Time spent waiting for a lock counts too: an upsert, a bulk upsert and a Studio import wait for locks on the table or the key, so on a busy table — other writes to the same table or the same key — even a small operation can run out of time.
To stay under the timeout, narrow the filters so fewer rows are scanned, page through a large read
with a cursor or
read it as a file, or split a
large write into smaller ones. Smaller writes are separate transactions and no longer roll back
together, so make each one safe to repeat — an upsert by key, for example — before re-running after
a failure. If the operations are as small as they can be, your operator can
raise DATA_TABLES_STATEMENT_TIMEOUT_MS or, for the bulk operations,
DATA_TABLES_BULK_STATEMENT_TIMEOUT_MS.
Next
The Data Table node
Every resource, operation and parameter a workflow can use.
Studio → Data Tables
Browsing, correcting and exporting rows by hand.
Agents
Letting an Agent read and write chosen tables.
In Packages
What happens to a table a Package installed.
Worked example
Extract once, check daily — the register pattern end to end.