Tables

Structured data tables scoped to your project — create, query, and manage tabular data that playbooks and agents can read and write.

Tables are structured tabular databases scoped to your project. Use them to store, query, and manage structured data that your playbooks and AI agents can read and write programmatically.

Creating a table

Open the Creation Picker (Cmd+N) and select Table, or use the create_data_store MCP tool from an agent session.

Each table has typed columns. Data is persisted in a per-project SQLite database.

Column types

TypeDescription
textPlain text
numberNumeric values
dateDate/time values
boolTrue/false
relationForeign key to another table

Columns also support format sub-types for richer display: email, currency, percent, rating, singleSelect, multiSelect, and rank. Select formats carry options with id, label, and color.

Natural keys

An optional natural key column marks the value that uniquely identifies a row (a date, an email, an external ID). It's what makes repeated writes idempotent instead of duplicating rows.

What a key collision does depends on which path is writing:

  • Playbook writeToStore steps upsert. A row whose key already exists is updated in place. This is what makes a periodic playbook safe to re-run.
  • The insert_data_store_rows MCP tool refuses by default. A row whose key already exists fails and the existing row is left untouched, because an agent inserting blind shouldn't be able to overwrite data it never read. Pass mode: "upsert" to insert-or-refresh, or mode: "update" to refresh only.

Views

Tables support alternate view modes:

  • Table — the default spreadsheet-style view.
  • Kanban — cards grouped by a singleSelect column.
  • Gantt — timeline view using start and end date columns.

Create and manage views via the UI or create_data_store_view / update_data_store_view MCP tools.

Querying data

Use the query_data_store MCP tool to filter, aggregate, group, sort and page a table — addressing columns and select options by their display names, not internal ids. It returns JSON, and it's the way to get an exact count without pulling rows:

{ "store": "Internal Tickets",
  "select": [{ "column": "Status" }, { "agg": "count", "as": "n" }],
  "group_by": ["Status"] }

run_data_query remains available for hand-written UUID-keyed query specs and cross-table joins.

Queries can also be embedded directly into notes as live chart/table blocks — the note auto-refreshes when the underlying data changes.

MCP tools

ToolDescription
create_data_storeCreate a new table
describe_data_storeGet a table's schema — columns, types, select options — with no rows
get_data_storeGet a table's schema plus one page of rows (limit / offset / next_offset)
list_data_storesList all tables in the project
add_data_store_columnAdd a column
update_data_store_columnModify a column's type or options
delete_data_store_columnRemove a column
insert_data_store_rowsInsert rows — refuses a natural-key collision unless you pass mode: "upsert"
update_data_store_rowUpdate a specific row
update_data_store_rowsBulk-update rows — a list of per-row changes, or where+set to change every matching row
delete_data_store_rowDelete a row
query_data_storeFilter, aggregate, group, sort and page by column display name
run_data_queryQuery with filters, sort, and projection (raw UUID-keyed spec; supports joins)
create_data_store_viewCreate a Kanban or Gantt view

Playbook integration

Two dedicated playbook step types work with tables:

  • writeToStore — upserts rows with natural-key-aware writes. Validates column IDs against the live schema.
  • readFromStore — queries with column filters and limits. Returns rows as step output for downstream steps to consume.