Skip to main content

Tables

Tables are structured data tables that agents build row by row. An agent researches, analyzes, or monitors something, and appends each finding as a row. The human reviews the results in a familiar spreadsheet-like view.

Relationship to Artifacts

A table is an artifact with type: "table". It gets the same persistent URL, sharing permissions, and access controls as any other artifact, standalone or inside a workspace. In a workspace, row writes carry a row revision precondition like every other workspace write — see Table Commands. The difference is in the content model: instead of a document body, a table has a schema (columns) and rows.
The returned URL works exactly like any artifact URL — shareable and accessible via content negotiation.

From a CSV file

If you already have the data in CSV form, skip writing a schema by hand:
The backend parses the CSV and returns a table with schema and rows populated in one call. See CSV Artifacts for the full import flow and how CSV and table primitives differ.

Schema

Every table has a schema that defines its columns. Each column has a name and a type: A column name is 1 to 256 characters, with no leading or trailing space and no comma or square bracket, and is unique in the table. Natural spreadsheet names such as Estado (funil) work, and sort and filter like any other. For enum columns, define the allowed values in the schema. Mark one column unique: true to make it the table’s key: storage then refuses two rows with the same value, and the key is the column you upsert on. A table has at most one key:
The key compares the exact stored text: "100" and "100.0" are different keys, while the number 1 and the string "1" are the same key. A key value is a string, number or boolean of at most 1024 bytes. Adding or moving the key later (a metadata update with a new schema) is refused with 409 if the column’s values already repeat.

Schema Auto-Expansion

Schemas grow automatically. When an agent appends a row with a column that does not exist in the schema, the column is added as text type. This keeps agents moving without requiring schema migrations — the agent discovers new fields during research and the table adapts. A table created with --strict turns this off (see Loose Enforcement).

Row Operations

Tables support four operations on rows: Append — Add one or more rows in a single request. This is the primary write operation. Agents call this repeatedly as they discover findings.
--upsert updates the row whose key value matches, or inserts it — idempotent in one call, with no check-then-write race. It matches on the table’s key (a table without one refuses NO_TABLE_KEY), and every row must carry it. A key repeated within one call, or (without --upsert) a key a stored row already holds, is refused with 409 DUPLICATE_UNIQUE_VALUE before any row is written. List — Paginate through rows with cursor-based pagination. Readable with the same rules as the table artifact itself: no key needed for link and public tables (rip table rows reads them without signing in). Supports server-side sorting and filtering.
Update — Modify a single row by its ID; the row names its table. Useful for correcting or enriching data after initial append.
Delete — Remove one or more rows by their IDs, all of one table.

The Agent Workflow

The typical flow:
  1. Agent creates a table with a schema defining the columns it plans to fill
  2. Agent researches, appending rows as findings come in — one API call per batch
  3. Human opens the table URL, sees a live table of results
  4. Human (or operator) reviews or edits rows from the dashboard
  5. Agent continues appending — the table grows over time
Tables work well for research tasks, monitoring results, competitive analysis, lead lists, audit findings, and any workflow where an agent produces structured records incrementally.

Sorting and Filtering

The GET rows endpoint supports server-side sorting and filtering via query parameters. Sort by any column with type-aware ordering (numeric sort for number columns, chronological for date):
Filter rows by column. Multiple filters are ANDed. The key may carry an operator — eq (default), lt, lte, gt, gte, ne, in (comma-separated), contains, starts — and comparisons use the column’s declared type:
A filter or sort on a column the table doesn’t have is an error that names the parameter (field) and lists the columns you can use (available). fields=slug,title limits the returned columns and include_total=true adds the matching row count. Sorting and filtering work with cursor pagination — the cursor remains stable across pages.

Loose Enforcement

By default, column types are display hints, not hard constraints. The backend does not reject a row because a value doesn’t match its declared type. The frontend degrades gracefully: a non-URL in a url column renders as plain text, a string in a number column renders as-is. This keeps agents frictionless — they append data without worrying about type validation — while giving the frontend enough information to render smart controls (checkboxes for booleans, date pickers for dates, dropdowns for enums). Create the table with --strict (rip artifact publish --type table --strict) when you want enforcement: row writes then reject unknown columns and type-mismatched values. The key (unique: true) is enforced either way — a duplicate value is refused with 409.

No Versioning

Unlike document artifacts, tables do not support versioning. There is no “publish a new version” — the table is a living dataset. Rows are appended, updated, or deleted in place. The artifact URL always reflects the current state of all rows. If you need versioned tabular data — a snapshot you can re-publish and, with its history on, compare against earlier versions — use a CSV artifact instead. CSV and table are complementary: CSV for snapshots, tables for living data.

Export

For heavy data processing, export the table to CSV or JSON:
Agents that need to do bulk analysis can pull the full dataset, process it locally, and publish the results as a separate artifact.