Tables API

Tables provide structured data storage for agents and users. Each table has typed columns, queryable rows with filtering and sorting, import/export support, automation triggers, chart visualizations, and full change history with rollback.

Quick Start

Create a table, add columns, insert rows, and query with filters — a typical integration flow:

// 1. Create a table with initial columns
const table: { id: string; name: string } = await fetch('/api/tables', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  name: 'Customer Feedback',
  projectId: 'proj-8f3a-4b2c',
  columns: [
    { name: 'customer', displayName: 'Customer', dataType: 'text', position: 0 },
    { name: 'rating', displayName: 'Rating', dataType: 'number', position: 1 },
    { name: 'feedback', displayName: 'Feedback', dataType: 'text', position: 2 },
  ],
}),
}).then((r) => r.json());
// → { id: "tbl-6c1d-9e7f", name: "Customer Feedback", ... }

// 2. Add a status column after creation
await fetch(`/api/tables/${table.id}/columns`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  name: 'status',
  displayName: 'Status',
  dataType: 'select',
  config: { options: ['open', 'in_progress', 'closed'] },
}),
});

// 3. Insert rows in batch (up to 1000 per request)
await fetch(`/api/tables/${table.id}/rows`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  rows: [
    { data: { customer: 'Acme Corp', rating: 5, feedback: 'Excellent', status: 'closed' } },
    { data: { customer: 'Globex Inc', rating: 3, feedback: 'Average', status: 'open' } },
    { data: { customer: 'Initech', rating: 4, feedback: 'Good support', status: 'closed' } },
  ],
}),
});

// 4. Query with filters — find high-rated feedback
const filters: string = JSON.stringify([{ column: 'rating', operator: 'gte', value: 4 }]);
const rows: Array<{ data: Record<string, unknown> }> = await fetch(
`/api/tables/${table.id}/rows?filters=${encodeURIComponent(filters)}`
).then((r) => r.json());
console.log(`${rows.length} rows with rating >= 4`);

Base URL

/api/tables

Endpoints

List Tables

GET /api/tables

Retrieve all tables with optional filtering by project, source, or search term.

Query Parameters

Param Type Req Description
projectId string Filter by project ID
source enum manual, imported, agent, or template
search string Search by table name

List all tables for a project — useful for building a data browser or selecting a table to query:

// List all tables in a project
const response: Response = await fetch('/api/tables?projectId=proj-8f3a-4b2c');
const tables: Array<{ id: string; name: string; source: string }> = await response.json();

tables.forEach((t) => console.log(`${t.name} (${t.source}) — ${t.id}`));

Example response:

[
  {
    "id": "tbl-6c1d-9e7f",
    "name": "Customer Feedback",
    "description": null,
    "projectId": "proj-8f3a-4b2c",
    "source": "manual",
    "rowCount": 42,
    "createdAt": "2026-04-03T10:00:00.000Z",
    "updatedAt": "2026-04-03T11:30:00.000Z"
  }
]

Create Table

POST /api/tables

Create a new table with optional column definitions. If a templateId is provided, the table is cloned from that template instead.

Request Body

FieldTypeReqDescription
namestring*Table name (1-256 characters)
descriptionstringTable description (max 1024 characters)
projectIdstring (UUID)Link to a project
columnsColumnDef[]Initial column definitions (see Column Schema below)
sourceenummanual, imported, agent, or template
templateIdstring (UUID)Clone from an existing template

Response 201 Created — New table object

Errors: 400 — Zod validation failure

Create a table with typed columns — columns define the schema that rows must follow:

// Create a table with typed columns
const response: Response = await fetch('/api/tables', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  name: 'Customer Feedback',
  projectId: 'proj-8f3a-4b2c',
  columns: [
    { name: 'customer', displayName: 'Customer', dataType: 'text', position: 0 },
    { name: 'rating', displayName: 'Rating', dataType: 'number', position: 1 },
    { name: 'feedback', displayName: 'Feedback', dataType: 'text', position: 2 },
  ],
}),
});
const table: { id: string } = await response.json();

console.log(table.id); // "tbl-6c1d-9e7f"

Example response:

{
  "id": "tbl-6c1d-9e7f",
  "name": "Customer Feedback",
  "projectId": "proj-8f3a-4b2c",
  "source": "manual",
  "columns": [
    { "id": "col-a1b2", "name": "customer", "displayName": "Customer", "dataType": "text", "position": 0 },
    { "id": "col-c3d4", "name": "rating", "displayName": "Rating", "dataType": "number", "position": 1 },
    { "id": "col-e5f6", "name": "feedback", "displayName": "Feedback", "dataType": "text", "position": 2 }
  ],
  "createdAt": "2026-04-03T10:00:00.000Z"
}

Clone from Template

POST /api/tables

Create a table by cloning a template. Provide templateId in the request body to use this path.

Request Body

FieldTypeReqDescription
templateIdstring (UUID)*Template to clone
namestring*Name for the new table (1-256 characters)
projectIdstring (UUID)Link to a project
includeSampleDatabooleanInclude sample rows from the template

Clone a template to get a pre-configured table with columns and optional sample data:

// Clone a template with sample data included
const response: Response = await fetch('/api/tables', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  templateId: 'tpl-4a2b-8c9d',
  name: 'Q3 Budget Tracker',
  projectId: 'proj-8f3a-4b2c',
  includeSampleData: true,
}),
});
const table: { id: string; name: string } = await response.json();

Get Table

GET /api/tables/{id}

Retrieve a single table by ID including its column definitions.

Response 200 — Table object with columns array

// Fetch table metadata and column definitions
const table: { name: string; columns: Array<{ displayName: string; dataType: string }> } =
await fetch('/api/tables/tbl-6c1d-9e7f').then((r) => r.json());

console.log(`${table.name} — ${table.columns.length} columns`);
table.columns.forEach((c) => console.log(`  ${c.displayName} (${c.dataType})`));

Errors: 404{ "error": "Table not found" }

Update Table

PATCH /api/tables/{id}

Update table name, description, or project association.

Request Body (all fields optional)

FieldTypeReqDescription
namestringUpdated name (1-256 chars)
descriptionstringUpdated description (max 1024 chars)
projectIdstring (UUID) | nullReassign or unlink project

Response 200 — Updated table object

Errors: 400 — Validation failure, 404 — Not found

// Update table name
await fetch('/api/tables/tbl-6c1d-9e7f', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Customer Feedback v2' }),
});

Delete Table

DELETE /api/tables/{id}

Permanently delete a table and all its rows, columns, triggers, charts, and history.

Response 204 No Content

Errors: 404 — Not found

// Delete a table and all associated data
await fetch('/api/tables/tbl-6c1d-9e7f', { method: 'DELETE' });

Rows

List Rows

GET /api/tables/{id}/rows

Query rows with filtering, sorting, and pagination. Filters and sorts are passed as JSON-encoded query parameters.

Query Parameters

Param Type Req Description
limit number Max rows to return (1-1000)
offset number Number of rows to skip
filters JSON string Array of FilterSpec objects (see Filtering below)
sorts JSON string Array of SortSpec objects (see Sorting below)

Query rows with filters and pagination — useful for building data views or feeding data to agents:

// Query with filters and sorting
const filters: string = JSON.stringify([
{ column: 'rating', operator: 'gte', value: 4 },
{ column: 'status', operator: 'eq', value: 'open' },
]);
const sorts: string = JSON.stringify([
{ column: 'rating', direction: 'desc' },
]);

const rows: Array<{ data: { customer: string; rating: number; feedback: string } }> = await fetch(
`/api/tables/tbl-6c1d-9e7f/rows?limit=50&filters=${encodeURIComponent(filters)}&sorts=${encodeURIComponent(sorts)}`
).then((r) => r.json());

rows.forEach((row) => {
console.log(`${row.data.customer}: ${row.data.rating}/5 — ${row.data.feedback}`);
});

Example response:

[
  {
    "id": "row-a1b2-c3d4",
    "tableId": "tbl-6c1d-9e7f",
    "data": {
      "customer": "Acme Corp",
      "rating": 5,
      "feedback": "Excellent support and fast resolution",
      "status": "open"
    },
    "createdBy": "claude-code",
    "createdAt": "2026-04-03T10:30:00.000Z",
    "updatedAt": "2026-04-03T10:30:00.000Z"
  }
]

Add Rows

POST /api/tables/{id}/rows

Insert one or more rows into a table. Accepts a batch of up to 1000 rows per request.

Request Body

FieldTypeReqDescription
rowsArray*Array of row objects (1-1000 items)
rows[].dataRecord<string, unknown>*Column name to value mapping
rows[].createdBystringCreator identifier (e.g. agent name)

Response 201 Created — Array of created rows

Insert a batch of rows — agents often use this to populate tables with structured results:

// Batch insert rows (up to 1000 per request)
const result: Array<{ id: string }> = await fetch('/api/tables/tbl-6c1d-9e7f/rows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  rows: [
    { data: { customer: 'Acme Corp', rating: 5, feedback: 'Excellent' }, createdBy: 'claude-code' },
    { data: { customer: 'Globex Inc', rating: 3, feedback: 'Average' }, createdBy: 'claude-code' },
  ],
}),
}).then((r) => r.json());

console.log(`Inserted ${result.length} rows`);

Errors: 400 — Validation failure, 404 — Table not found

Update Row

PATCH /api/tables/{id}/rows/{rowId}

Update the data for a single row. The entire data object is replaced.

Request Body

FieldTypeReqDescription
dataRecord<string, unknown>*Updated column name to value mapping

Response 200 — Updated row

Replace a row’s data — note that the entire data object is replaced, not merged:

// Update a row — full data replacement
await fetch('/api/tables/tbl-6c1d-9e7f/rows/row-a1b2-c3d4', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  data: { customer: 'Acme Corp', rating: 4, feedback: 'Good, improved response time', status: 'closed' },
}),
});

Errors: 400 — Validation failure, 404 — Row not found

Delete Row

DELETE /api/tables/{id}/rows/{rowId}

Permanently delete a single row.

Response 204 No Content

// Delete a single row
await fetch('/api/tables/tbl-6c1d-9e7f/rows/row-a1b2-c3d4', { method: 'DELETE' });

Columns

Add Column

POST /api/tables/{id}/columns

Add a new column to an existing table.

Request Body

FieldTypeReqDescription
namestring*Column key (1-64 chars, unique per table)
displayNamestring*Display label (1-128 chars)
dataTypeenum*text, number, boolean, date, select, url, email, computed, or relation
requiredbooleanWhether the column is required
defaultValuestring | nullDefault value for new rows
configColumnConfigType-specific config (see Column Config below)

Response 201 Created — New column object

Add a select column with predefined options — great for status tracking:

// Add a select column with options
const column: { id: string; name: string } = await fetch('/api/tables/tbl-6c1d-9e7f/columns', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  name: 'status',
  displayName: 'Status',
  dataType: 'select',
  config: { options: ['open', 'in_progress', 'closed'] },
}),
}).then((r) => r.json());

Example response:

{
  "id": "col-g7h8",
  "tableId": "tbl-6c1d-9e7f",
  "name": "status",
  "displayName": "Status",
  "dataType": "select",
  "position": 3,
  "config": { "options": ["open", "in_progress", "closed"] },
  "createdAt": "2026-04-03T10:05:00.000Z"
}

Reorder Columns

PATCH /api/tables/{id}/columns

Reorder all columns by providing column IDs in the desired order.

Request Body

FieldTypeReqDescription
columnIdsstring[]*Column IDs in the desired display order

Response 200 — Reordered columns array

// Move status column to the front
await fetch('/api/tables/tbl-6c1d-9e7f/columns', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  columnIds: ['col-g7h8', 'col-a1b2', 'col-c3d4', 'col-e5f6'],
}),
});

Import & Export

Import Data

POST /api/tables/{id}/import

Import structured data from a document into the table. Supports a preview mode to inspect inferred columns and sample rows before committing. Column types are auto-inferred from the first 100 rows.

Request Body

FieldTypeReqDescription
documentIdstring (UUID)*Source document to import from
previewbooleanSet true to preview without importing
columnMappingArrayOverride inferred column names, types, or skip columns
columnMapping[].namestring*Column key
columnMapping[].displayNamestring*Display label
columnMapping[].dataTypestring*Column data type
columnMapping[].skipbooleanSet true to skip this column

Preview Response

FieldTypeReqDescription
headersstring[]*Detected column headers
sampleRowsArray*First 10 rows of data
totalRowsnumber*Total row count in source
inferredColumnsColumnDef[]*Auto-inferred column definitions

Import Response

FieldTypeReqDescription
importIdstring*Audit record ID
rowsImportednumber*Successfully imported rows
rowsSkippednumber*Skipped rows (validation errors)
errorsArray*First 20 error details
columnsColumnDef[]*Final column definitions used

Preview an import before committing — inspect inferred column types and sample data, then execute:

// Preview first, then import
const preview: { totalRows: number; headers: string[] } = await fetch(
'/api/tables/tbl-6c1d-9e7f/import',
{
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ documentId: 'doc-7b2e-3f9a', preview: true }),
}
).then((r) => r.json());

console.log(`${preview.totalRows} rows, ${preview.headers.length} columns detected`);

// Execute the import
const result: { rowsImported: number; rowsSkipped: number } = await fetch(
'/api/tables/tbl-6c1d-9e7f/import',
{
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ documentId: 'doc-7b2e-3f9a' }),
}
).then((r) => r.json());

console.log(`Imported ${result.rowsImported}, skipped ${result.rowsSkipped}`);

Example preview response:

{
  "headers": ["customer", "rating", "feedback"],
  "sampleRows": [
    { "customer": "Acme Corp", "rating": "5", "feedback": "Excellent" }
  ],
  "totalRows": 150,
  "inferredColumns": [
    { "name": "customer", "displayName": "customer", "dataType": "text" },
    { "name": "rating", "displayName": "rating", "dataType": "number" },
    { "name": "feedback", "displayName": "feedback", "dataType": "text" }
  ]
}

Export Table

GET /api/tables/{id}/export

Download the entire table as CSV, JSON, or XLSX. Returns up to 10,000 rows.

Query Parameters

Param Type Req Description
format enum csv (default), json, or xlsx

Response 200 — File download with appropriate Content-Type and Content-Disposition headers

Export a table for external use — CSV for spreadsheets, JSON for programmatic access, XLSX for stakeholders:

// Download as CSV and trigger browser save
const res: Response = await fetch('/api/tables/tbl-6c1d-9e7f/export?format=csv');
const blob: Blob = await res.blob();
const url: string = URL.createObjectURL(blob);

const link: HTMLAnchorElement = document.createElement('a');
link.href = url;
link.download = 'customer-feedback.csv';
link.click();

Triggers

List Triggers

GET /api/tables/{id}/triggers

List all automation triggers configured for a table.

Response Body (Array)

FieldTypeReqDescription
idstring (UUID)*Trigger identifier
tableIdstring (UUID)*Parent table ID
namestring*Trigger name
triggerEventenum*row_added, row_updated, or row_deleted
conditionJSON | nullOptional condition expression
actionTypeenum*run_workflow or create_task
actionConfigJSON*Action-specific configuration
statusenum*active or paused
fireCountnumber*Times this trigger has fired

List triggers to see what automations are active on a table:

// Check which triggers are active
const triggers: Array<{ name: string; status: string; triggerEvent: string; actionType: string; fireCount: number }> =
await fetch('/api/tables/tbl-6c1d-9e7f/triggers').then((r) => r.json());

triggers.forEach((t) => {
console.log(`${t.name} [${t.status}] — ${t.triggerEvent} → ${t.actionType} (fired ${t.fireCount}x)`);
});

Example response:

[
  {
    "id": "trg-5e4d-3c2b",
    "tableId": "tbl-6c1d-9e7f",
    "name": "Auto-triage feedback",
    "triggerEvent": "row_added",
    "condition": null,
    "actionType": "run_workflow",
    "actionConfig": { "workflowId": "wf-2a3b-4c5d" },
    "status": "active",
    "fireCount": 28
  }
]

Create Trigger

POST /api/tables/{id}/triggers

Create a new automation trigger that fires when rows are added, updated, or deleted.

Request Body

FieldTypeReqDescription
namestring*Trigger name
triggerEventenum*row_added, row_updated, or row_deleted
conditionobjectCondition that must be met for the trigger to fire
actionTypeenum*run_workflow or create_task
actionConfigobject*Action configuration (e.g. workflowId, task template)

Response 201 Created — New trigger object

Create a trigger that automatically runs a workflow when new feedback is added:

// Auto-run a triage workflow when new rows are added
const trigger: { id: string; name: string } = await fetch('/api/tables/tbl-6c1d-9e7f/triggers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  name: 'Auto-triage feedback',
  triggerEvent: 'row_added',
  actionType: 'run_workflow',
  actionConfig: { workflowId: 'wf-2a3b-4c5d' },
}),
}).then((r) => r.json());

Update Trigger

PATCH /api/tables/{id}/triggers/{triggerId}

Update a trigger's name, event, condition, action, or status.

Request Body (all fields optional)

FieldTypeReqDescription
namestringUpdated name
triggerEventenumrow_added, row_updated, or row_deleted
statusenumactive or paused
conditionobject | nullUpdated condition (null to remove)
actionTypeenumrun_workflow or create_task
actionConfigobjectUpdated action configuration

Response 200 — Updated trigger object

Errors: 404 — Trigger not found

Pause a trigger temporarily during maintenance:

// Pause a trigger during maintenance
await fetch('/api/tables/tbl-6c1d-9e7f/triggers/trg-5e4d-3c2b', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'paused' }),
});

Delete Trigger

DELETE /api/tables/{id}/triggers/{triggerId}

Permanently delete a trigger.

Response 204 No Content

Errors: 404 — Trigger not found

// Delete a trigger
await fetch('/api/tables/tbl-6c1d-9e7f/triggers/trg-5e4d-3c2b', { method: 'DELETE' });

Charts

List Charts

GET /api/tables/{id}/charts

List all chart visualizations saved for a table.

Response Body (Array)

FieldTypeReqDescription
idstring (UUID)*Chart view ID
namestring*Chart title
configobject*Chart configuration (type, xColumn, yColumn, aggregation)
createdAtISO 8601*Creation timestamp
// List all charts for a table
const charts: Array<{ name: string; config: { type: string } }> =
await fetch('/api/tables/tbl-6c1d-9e7f/charts').then((r) => r.json());
charts.forEach((c) => console.log(`${c.name} (${c.config.type})`));

Create Chart

POST /api/tables/{id}/charts

Save a new chart visualization for a table.

Request Body

FieldTypeReqDescription
typestring*Chart type (e.g. bar, line, pie, scatter)
titlestring*Chart display title
xColumnstring*Column name for X axis
yColumnstringColumn name for Y axis
aggregationstringAggregation function (e.g. sum, count, avg)

Response 201 Created{ "id": "...", "name": "..." }

Create a bar chart showing the distribution of customer ratings:

// Create a ratings distribution chart
const chart: { id: string; name: string } = await fetch('/api/tables/tbl-6c1d-9e7f/charts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  type: 'bar',
  title: 'Ratings Distribution',
  xColumn: 'rating',
  aggregation: 'count',
}),
}).then((r) => r.json());

Update Chart

PATCH /api/tables/{id}/charts/{chartId}

Update a chart's title, type, axes, or aggregation. Config fields are merged with the existing configuration.

Request Body (all fields optional)

FieldTypeReqDescription
titlestringUpdated chart title
typestringUpdated chart type
xColumnstringUpdated X axis column
yColumnstringUpdated Y axis column
aggregationstringUpdated aggregation function

Response 200 — Updated chart object with parsed config

Errors: 404 — Chart not found

// Switch to pie chart
await fetch('/api/tables/tbl-6c1d-9e7f/charts/chart-a1b2-c3d4', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'pie', title: 'Ratings Breakdown' }),
});

Delete Chart

DELETE /api/tables/{id}/charts/{chartId}

Permanently delete a chart visualization.

Response 204 No Content

Errors: 404 — Chart not found

// Delete a chart
await fetch('/api/tables/tbl-6c1d-9e7f/charts/chart-a1b2-c3d4', { method: 'DELETE' });

History

Table History

GET /api/tables/{id}/history

Retrieve the change history for a table (row additions, updates, deletions).

Query Parameters

Param Type Req Description
limit number Max entries to return (default 100, max 500)
// Fetch recent change history
const history: Array<{ action: string; rowId: string; createdAt: string }> =
await fetch('/api/tables/tbl-6c1d-9e7f/history?limit=50').then((r) => r.json());

history.forEach((entry) => {
console.log(`[${entry.action}] row ${entry.rowId} at ${entry.createdAt}`);
});

Row History

GET /api/tables/{id}/rows/{rowId}/history

Retrieve the change history for a specific row, showing each version of its data.

Query Parameters

Param Type Req Description
limit number Max entries to return (default 50, max 200)
// View how a row changed over time
const history: Array<{ version: number; data: { rating: number }; createdAt: string }> =
await fetch('/api/tables/tbl-6c1d-9e7f/rows/row-a1b2-c3d4/history').then((r) => r.json());

history.forEach((entry) => {
console.log(`v${entry.version}: rating=${entry.data.rating} at ${entry.createdAt}`);
});

Rollback Row

POST /api/tables/{id}/rows/{rowId}/history

Rollback a row to a previous version by providing a history entry ID.

Request Body

FieldTypeReqDescription
historyEntryIdstring (UUID)*History entry to rollback to

Response 200{ "ok": true }

Rollback a row to a previous version — useful for undoing accidental edits:

// Rollback a row to a previous version
await fetch('/api/tables/tbl-6c1d-9e7f/rows/row-a1b2-c3d4/history', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ historyEntryId: 'hist-9f8e-7d6c' }),
});

Errors: 400 — Missing historyEntryId, 404 — History entry not found


Templates

List Templates

GET /api/tables/templates

List available table templates, optionally filtered by category or scope.

Query Parameters

Param Type Req Description
category enum business, personal, pm, finance, or content
scope enum system or user
// List project management templates
const { templates }: { templates: Array<{ name: string; scope: string; description: string }> } =
await fetch('/api/tables/templates?category=pm').then((r) => r.json());

templates.forEach((t) => console.log(`${t.name} [${t.scope}] — ${t.description}`));

Save as Template

POST /api/tables/templates

Save an existing table as a reusable user-scoped template. Optionally includes the first 5 rows as sample data.

Request Body

FieldTypeReqDescription
tableIdstring (UUID)*Source table to save as template
namestring*Template name
descriptionstringTemplate description (defaults to table description)
categoryenumbusiness, personal, pm, finance, or content
includeSampleDatabooleanInclude first 5 rows as sample data

Response 201 Created{ "id": "...", "name": "..." }

Save a well-designed table as a reusable template for your team:

// Save a table as a reusable template
const template: { id: string } = await fetch('/api/tables/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  tableId: 'tbl-6c1d-9e7f',
  name: 'Sprint Planning Board',
  category: 'pm',
  includeSampleData: true,
}),
}).then((r) => r.json());

console.log(`Template saved: ${template.id}`);

Column Config Reference

Config FieldApplies ToDescription
optionsselectArray of allowed option strings
formulacomputedFormula expression string
formulaTypecomputedarithmetic, text_concat, date_diff, conditional, or aggregate
resultTypecomputedExpected result data type
dependenciescomputedColumn names the formula depends on
targetTableIdrelationID of the related table
displayColumnrelationColumn name to display from the related table

Filter Operators

OperatorDescription
eqEqual to
neqNot equal to
gt / gteGreater than / greater than or equal
lt / lteLess than / less than or equal
containsString contains
starts_withString starts with
inValue is in array
is_emptyValue is null or empty
is_not_emptyValue is not null or empty