Base & Tables Tools
Tools for managing bases, tables, fields, and records.
Available Tools
tables_createDatabaseCreate a new database (base). Params: name (required), description, icon, color (hex like "#3B82F6"). Returns the created database with its ID.
Input Schema
name | stringrequired | Name for the new database/base |
description | string | Optional description of the database |
icon | string | Icon name (e.g., "database", "folder") |
color | string | Hex color code (e.g., "#3B82F6") |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_createDatabase",
"arguments": {
"name": "example",
"description": "example",
"icon": "example"
}
}
}tables_listDatabasesList all databases (bases) for the current user. Returns array with id, name, description, tables[]. Use this first to get database IDs.
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_listDatabases",
"arguments": {}
}
}tables_getDatabaseByIdGet a specific database by ID. Param "id" is a database ID from listDatabases. Returns database with its tables.
Input Schema
id | stringrequired | Database ID from listDatabases |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getDatabaseById",
"arguments": {
"id": "example"
}
}
}tables_updateDatabaseUpdate a database. Params: id (database ID), name, description, icon, color. Only provided fields are updated.
Input Schema
id | stringrequired | Database ID from listDatabases |
name | string | New name for the database |
description | string | New description |
icon | string | New icon name |
color | string | New hex color code |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateDatabase",
"arguments": {
"id": "example",
"name": "example",
"description": "example"
}
}
}tables_deleteDatabaseDelete a database and all its tables. Param "id" is the database ID. WARNING: This is permanent. Act only on what the person named or picked.
Input Schema
id | stringrequired | Database ID to delete. WARNING: This permanently deletes all tables and data. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_deleteDatabase",
"arguments": {
"id": "example"
}
}
}tables_createTableCreate a new table. Params: databaseId (required, from listDatabases), name (required), description, icon, color. A table a site's pages use goes in that site's linked base (site_getProject → linkedBase.id); a site reads another base's table only through an account function (functions.invokeAccount; functions_create + grants.bases + accessRules.execute). Recreating tables in another base, or copying or moving rows between bases, is work beyond the request: ask the person first. A base another site links: offer the account-function route (no copy, recommended) or the move. A base no site links (no account function reaches it): offer the copy into this site's base (tables_duplicateTable with includeRows: true; recommended, the only way the site can show it) or leaving it.
Input Schema
databaseId | stringrequired | Database ID from listDatabases where the table will be created |
name | stringrequired | Name for the new table |
description | string | Optional description of the table |
icon | string | Icon name (e.g., "table", "users") |
color | string | Hex color code (e.g., "#10B981") |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_createTable",
"arguments": {
"databaseId": "example",
"name": "example",
"description": "example"
}
}
}tables_listTablesList all tables across all databases. Returns array with id, name, databaseId. Use getTableById for full details.
Input Schema
databaseId | string | Limit to one base — omit for all tables across all bases |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_listTables",
"arguments": {
"databaseId": "example"
}
}
}tables_getViewsByTableIdAlias for getViews. Param "tableId".
Input Schema
tableId | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getViewsByTableId",
"arguments": {
"tableId": "example"
}
}
}tables_getFieldsByTableIdGet all fields/columns for a table. Param "tableId" from listTables. Returns array with field id, name, fieldType, options.
Input Schema
tableId | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getFieldsByTableId",
"arguments": {
"tableId": "example"
}
}
}tables_getTableByIdGet table with fields and views. Param "id" is a table ID from listTables. Returns table with fields[], views[], database info.
Input Schema
id | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getTableById",
"arguments": {
"id": "example"
}
}
}tables_updateTableUpdate table properties. Params: id (table ID), name, description, icon, color. Renaming a table a function with Full data access reads by name (or renaming another into that name) is prepared as a card a person taps: the answer carries pendingChange and nothing changes until they approve. A function with Full data access whose code reaches the tables counts as reading every table of its base.
Input Schema
id | stringrequired | Table ID from listTables |
name | string | New name for the table |
description | string | New description |
icon | string | New icon name |
color | string | New hex color code |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateTable",
"arguments": {
"id": "example",
"name": "example",
"description": "example"
}
}
}tables_deleteTableDelete a table and all its rows. Param "id" is the table ID. WARNING: This is permanent. Deleting a table a function with Full data access reads by name is prepared as a card a person taps (pendingChange; it can't be undone once applied). Act only on what the person named or picked.
Input Schema
id | stringrequired | Table ID to delete. WARNING: This permanently deletes all rows and fields. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_deleteTable",
"arguments": {
"id": "example"
}
}
}tables_createFieldCreate a new field/column. Params: tableId (required), name (required), fieldType (required: "text", "longtext", "number", "email", "date", "datetime", "checkbox", "select", "multiselect", "url", "phone", "currency", "percent", "rating", "duration", "file", "link", "lookup", "rollup", "formula", "button", "status", "slug", "hash", "color", "json", "secret", "created_time", "updated_time" — note: file uploads use "file", there is no "attachment" type; "secret" = encrypted at rest and MASKED in every read (grid, API, exports, even function reads); nobody reads it back, the owner included, it can only be replaced — right for sensitive data IN records, but code cannot use a secret field's value, so API keys belong in env/Secrets, not here). Optional: description, unique, defaultValue, options (for select/multiselect: a FLAT array [{value, label, color?}] — label is required, color is #RRGGBB; no "choices" wrapper and no "id"). Note: "required" is not supported — pass false or omit it.
Input Schema
tableId | stringrequired | Table ID from listTables |
name | stringrequired | Name for the new field/column |
fieldType | stringrequired | Field type: "text", "number", "select", "multiselect", "date", "datetime", "checkbox", "url", "email", "phone", "file", "formula", "rating", "currency", "percent", "duration", "status", "button", "link", "lookup", "rollup". READ-ONLY computed types (writes to them are dropped): "hash" = Hash ID, a read-only stable per-row identifier — it does NOT hash a value you supply (to store a hashed value, compute it yourself and use text/secret); "created_time"/"updated_time" = system timestamps; "formula"/"lookup"/"rollup" = computed. Numbers are exact only up to 2^53 — unsafe integers are rejected with guidance; store long IDs, account numbers, and IMEIs as text. |
description | string | Optional description of the field |
required | boolean | Must be false or omitted. Required fields are not supported - UI creates blank rows first. |
unique | boolean | Whether values must be unique (default: false) |
defaultValue | any | Default value for new rows |
options | array | For select/multiselect: [{value: "opt1", label: "Option 1", color: "#3b82f6"}] |
statusLabels | array | For status: [{value: "todo", label: "To Do", color: "#6b7280"}] |
formulaConfig | object | For formula. Values of a formula without NOW()/TODAY() are stored on the row, recalculated when inputs change (also through links) and filterable everywhere; with NOW()/TODAY() they are display-only and not filterable |
buttonConfig | object | For button: {actionType: "flow", flowId: "xxx"} |
numberFormatConfig | object | For number: {decimalPlaces: 2, useThousandSeparator: true} |
currencyFormatConfig | object | For currency: {currencySymbol: "$", symbolPosition: "before"} |
percentFormatConfig | object | For percent: {decimalPlaces: 1} |
lookupConfig | object | For lookup. Its COUNT counts NON-EMPTY VALUES (a rollup COUNT counts linked records). Scalar (non-chained) aggregations are filterable everywhere (view filters, filterRows, entities.filter()) |
rollupConfig | object | For rollup. COUNT counts LINKED RECORDS, empty values included (COUNT_FILLED = lookup COUNT). Scalar (non-chained) aggregations are filterable everywhere (view filters, filterRows, entities.filter()) |
linkedTableId | string | For link field: target table ID |
linkType | string | Link relationship type |
displayOrder | string | Where to put the column: a fractional position the grid made between two columns (e.g. "a0V"); anything else is refused. Leave empty to add it at the end. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_createField",
"arguments": {
"tableId": "example",
"name": "example",
"fieldType": "example"
}
}
}tables_updateFieldUpdate field properties. Params: id (field ID from getFieldsByTableId), name, fieldType, description, unique, options (flat [{value, label, color?}]), etc. Same field-type set as createField; "required" must be false or omitted. Renaming, retyping, or changing what it links to or looks up, a field of a table a function with Full data access reads by name is prepared as a card a person taps (pendingChange): send that change on its own.
Input Schema
id | stringrequired | Field ID from getFieldsByTableId |
name | string | New name for the field |
fieldType | string | Field type |
description | string | New description |
required | boolean | Must be false or omitted. Required fields are not supported - UI creates blank rows first. |
unique | boolean | Whether values must be unique |
defaultValue | any | Default value for new rows |
options | array | For select/multiselect: [{value: "opt1", label: "Option 1", color: "#3b82f6"}] |
statusLabels | array | For status: [{value: "todo", label: "To Do", color: "#6b7280"}] |
formulaConfig | object | For formula. Values of a formula without NOW()/TODAY() are stored on the row, recalculated when inputs change (also through links) and filterable everywhere; with NOW()/TODAY() they are display-only and not filterable |
linkedTableId | string | For link field: target table ID |
linkType | string | Link relationship type |
linkDisplayMode | string | How to display linked records |
linkDisplayFieldId | string | Which field to show from linked records |
allowMultipleLinks | boolean | Whether multiple links are allowed |
linkOnDelete | string | For link fields: what happens to linking rows when a linked record is deleted. RESTRICT blocks the delete; CASCADE deletes the linking rows too (chains, capped at 500 rows per delete); SET_NULL (default) just removes the connection. |
buttonConfig | object | For button: {actionType: "flow", flowId: "xxx"} |
numberFormatConfig | object | For number: {decimalPlaces: 2, useThousandSeparator: true} |
currencyFormatConfig | object | For currency: {currencySymbol: "$", symbolPosition: "before"} |
percentFormatConfig | object | For percent: {decimalPlaces: 1} |
lookupConfig | object | For lookup. Its COUNT counts NON-EMPTY VALUES (a rollup COUNT counts linked records). Scalar (non-chained) aggregations are filterable everywhere (view filters, filterRows, entities.filter()) |
rollupConfig | object | For rollup. COUNT counts LINKED RECORDS, empty values included (COUNT_FILLED = lookup COUNT). Scalar (non-chained) aggregations are filterable everywhere (view filters, filterRows, entities.filter()) |
displayOrder | number | Display order position |
isVisible | boolean | Whether field is visible |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateField",
"arguments": {
"id": "example",
"name": "example",
"fieldType": "example"
}
}
}tables_deleteFieldDelete a field/column. Param "id" is the field ID. WARNING: Deletes all data in this column. Deleting a field of a table a function with Full data access reads by name is prepared as a card a person taps (pendingChange; it can't be undone once applied). Act only on what the person named or picked.
Input Schema
id | stringrequired | Field ID to delete. WARNING: This permanently deletes all data in this column. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_deleteField",
"arguments": {
"id": "example"
}
}
}tables_createRowCreate a new row. Params: tableId (required), data (object keyed by field ID from getFieldsByTableId — field NAMES are also accepted and auto-normalized, but IDs are safest since names can change). Example: {tableId: "xxx", data: {"fieldId1": "text value", "fieldId2": 123}}.
Input Schema
tableId | stringrequired | Table ID from listTables |
data | objectrequired | Object where keys are FIELD IDs from getFieldsByTableId, values are cell values. Example: {"fieldId1": "text", "fieldId2": 123} |
rowOrder | string | Fractional order string for positioning (a key the grid made; anything else is refused). Leave empty to append at end. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_createRow",
"arguments": {
"tableId": "example",
"data": {},
"rowOrder": "example"
}
}
}tables_reportAnswer a numbers question about a table in one call (counts, totals, averages per slice) — never write SQL. Params: tableId; measures (1-8: {as, fn: count|countDistinct|sum|avg|min|max|median|ratio, field: {fieldId}, filter?, numerator?/denominator? (ratio: the "as" of two other measures), decimals? 0-6}); filter? (which rows count — the view filter language {logic:"and"|"or", conditions:[{fieldId, operator, value}]}, up to 30 conditions, a nested group counting as one, 3 levels); groupBy? (up to 3: {as, field: {fieldId}, bucket?: day|week|month|quarter|year|hour|dayOfWeek for date fields}); having? ({measure, operator, value}); sort? ({by: an "as", direction}); limit? (1-1000, default 100) and offset? (pages, hasMore says more exist); percentOfTotal? (the "as" of a count or sum: each slice's share of ALL slices, 0-1); timeZone? (IANA, default UTC; week starts Monday). A measure with a filter counts only its rows (win rate = ratio of a filtered count over count). Empty values form one null slice. Returns {columns, rows: [{groups, measures, percent?}], hasMore, total, timeZone, truncatedAt?}. Not yet possible: grouping through a linked record, lookup/rollup fields, running totals, period-over-period in one report, cohorts, two-level aggregates (use two reports). Sign-in and secret fields can never be used. Example: {tableId, filter:{logic:"and",conditions:[{fieldId:"<Status id>",operator:"equals",value:"Paid"}]}, groupBy:[{as:"month",field:{fieldId:"<Created id>"},bucket:"month"}], measures:[{as:"orders",fn:"count"},{as:"revenue",fn:"sum",field:{fieldId:"<Amount id>"}}]}.
Input Schema
filter | any | Which rows count: {logic: "and"|"or", conditions: [{fieldId, operator, value}]} — the view filter language |
groupBy | array | Up to 3 slices |
measures | arrayrequired | 1 to 8 numbers to work out per slice |
having | array | Keep only slices whose measure passes (up to 4) |
sort | array | Order by group or measure names (up to 3) |
limit | integer | Slices per answer, 1-1000 (default 100) |
offset | integer | Slices to skip, for the next page (max 100000) |
percentOfTotal | string | The `as` of a count or sum measure: adds each slice's share of the whole (0-1) |
timeZone | string | IANA time zone for date slices, e.g. "Europe/Berlin" (default UTC) |
tableId | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_report",
"arguments": {
"filter": {},
"groupBy": {},
"measures": {}
}
}
}tables_getRowCountGet total row count. Param "tableId". Returns {count: number}.
Input Schema
tableId | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getRowCount",
"arguments": {
"tableId": "example"
}
}
}tables_getRowsGet rows from a table. Params: tableId (required), limit (default 100), offset (default 0). Returns {rows: [{id, data: {fieldId: value}}], total}. Data keys are field IDs, not names.
Input Schema
tableId | stringrequired | Table ID from listTables |
limit | number | Rows to return, 1-1000 (default 100); outside the range is REJECTED, not clamped: page with offset for more |
offset | number | Rows to skip (paging) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getRows",
"arguments": {
"tableId": "example",
"limit": 0,
"offset": 0
}
}
}tables_updateRowUpdate a row. Params: id (row ID from getRows), data (partial object keyed by field ID; exact field names are also accepted). Only provided fields are updated.
Input Schema
id | stringrequired | Row ID from getRows |
data | objectrequired | Object with field IDs as keys and new values. Only provided fields are updated. |
metadata | object | Optional metadata to merge with existing |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateRow",
"arguments": {
"id": "example",
"data": {},
"metadata": {}
}
}
}tables_restoreRowUndo a row delete: bring back a row this same person deleted (with deleteRow or bulkDeleteRows) in the last ten minutes, at its old id, exactly as it was (its values are the copy kept when it was deleted). Params: tableId, rowId (the deleted row's id), rowOrder? (a row position the grid made, e.g. one taken from getRows; anything else is refused; default: where it was). Each delete can be undone once. A row deleted by someone else, longer ago, or never deleted answers "Row not found". Returns { id, tableId, data, rowOrder, createdAt, updatedAt } (secret values masked).
Input Schema
tableId | stringrequired | The table the row was deleted from |
rowId | stringrequired | The deleted row's id |
rowOrder | string | Where to put it back (a row position the grid made; a bad one is refused); default: where it was |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_restoreRow",
"arguments": {
"tableId": "example",
"rowId": "example",
"rowOrder": "example"
}
}
}tables_deleteRowDelete a row. Param "id" is the row ID. Act only on what the person named or picked.
Input Schema
id | stringrequired | Row ID to delete |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_deleteRow",
"arguments": {
"id": "example"
}
}
}tables_bulkDeleteRowsDelete multiple rows. Params: tableId, rowIds (array of row IDs). Act only on what the person named or picked.
Input Schema
tableId | stringrequired | |
rowIds | arrayrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_bulkDeleteRows",
"arguments": {
"tableId": "example",
"rowIds": {}
}
}
}tables_createViewCreate a new view. Params: tableId (required), name (required), viewType (required: "grid", "kanban", "calendar", "gallery", "form"), description, filterConfig, sortConfig, groupConfig, visibleFields (array of field IDs).
Input Schema
tableId | stringrequired | |
name | stringrequired | |
viewType | stringrequired | |
description | string | |
filterConfig | any | |
sortConfig | any | |
groupConfig | any | |
visibleFields | array | |
columnWidths | any | |
isDefault | boolean |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_createView",
"arguments": {
"tableId": "example",
"name": "example",
"viewType": "example"
}
}
}tables_updateViewUpdate view settings. Params: id (view ID), name, filterConfig, sortConfig, groupConfig, visibleFields, metadata (an object; a form's settings, e.g. theme and protection { requirePassword, password, limitSubmissions, maxSubmissions, closeFormAfterDate, closeDate }), etc. A form's password is stored hashed and never returned: leave protection.password out to keep it, send "" to remove it, a new value to replace it.
Input Schema
id | stringrequired | |
name | string | |
description | string | |
filterConfig | any | |
sortConfig | any | |
groupConfig | any | |
visibleFields | array | |
columnWidths | any | |
isDefault | boolean | |
metadata | any |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateView",
"arguments": {
"id": "example",
"name": "example",
"description": "example"
}
}
}tables_getViewsList all views for a table. Param "tableId". Returns array with id, name, viewType, filterConfig, sortConfig.
Input Schema
tableId | stringrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getViews",
"arguments": {
"tableId": "example"
}
}
}tables_reorderViewChange view position. Params: viewId, newOrderIndex (string).
Input Schema
viewId | stringrequired | |
newOrderIndex | stringrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_reorderView",
"arguments": {
"viewId": "example",
"newOrderIndex": "example"
}
}
}tables_duplicateViewCopy a view. Params: id (view ID to copy), name (optional, for the copy).
Input Schema
id | stringrequired | |
name | string |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_duplicateView",
"arguments": {
"id": "example",
"name": "example"
}
}
}tables_setDefaultViewSet which view opens by default. Params: id (view ID), tableId.
Input Schema
id | stringrequired | |
tableId | stringrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_setDefaultView",
"arguments": {
"id": "example",
"tableId": "example"
}
}
}tables_deleteViewDelete a view. Param "id" is the view ID. Act only on what the person named or picked.
Input Schema
id | stringrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_deleteView",
"arguments": {
"id": "example"
}
}
}tables_updateFieldOrderChange field position. Params: fieldId, newOrder (a position key made between the displayOrder of the two neighbouring fields from getFieldsByTableId; any other text is refused). To add a new field at the end, use createField and leave displayOrder out.
Input Schema
fieldId | stringrequired | |
newOrder | stringrequired | New fractional position of the column (a key the grid made; anything else is refused) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateFieldOrder",
"arguments": {
"fieldId": "example",
"newOrder": "example"
}
}
}tables_updateTableOrderChange table position in database. Params: tableId, newOrder (string like "a0", "b0" for fractional ordering).
Input Schema
tableId | stringrequired | |
newOrder | stringrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateTableOrder",
"arguments": {
"tableId": "example",
"newOrder": "example"
}
}
}tables_updateFieldWidthSet column width in pixels. Params: fieldId, width (number, e.g., 200).
Input Schema
fieldId | stringrequired | |
width | numberrequired |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateFieldWidth",
"arguments": {
"fieldId": "example",
"width": 0
}
}
}tables_updateRowOrderChange row position. Params: rowId, newOrder (fractional string).
Input Schema
rowId | stringrequired | Row ID to reorder |
newOrder | stringrequired | New fractional order string (a key the grid made; anything else is refused) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_updateRowOrder",
"arguments": {
"rowId": "example",
"newOrder": "example"
}
}
}tables_bulkUpdateRowsUpdate multiple rows in one call. Params: tableId, updates (array of {rowId, data}). Each update merges its data with the existing row — fields not included are preserved, identical semantics to updateRow. More efficient than calling updateRow in a loop.
Input Schema
tableId | stringrequired | Table ID from listTables |
updates | arrayrequired | Array of row updates |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_bulkUpdateRows",
"arguments": {
"tableId": "example",
"updates": {}
}
}
}tables_exportTableExport all table data. Param "tableId". Returns {tableName, fields[], rows[]} with human-readable field names as keys.
Input Schema
tableId | stringrequired | Table ID to export |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_exportTable",
"arguments": {
"tableId": "example"
}
}
}tables_importRowsBulk import rows. Params: tableId, rows (array of data objects), matchFieldsByName (default true - use field NAMES as keys instead of IDs), fieldDefinitions (optional, to create new fields). When matchFieldsByName=true, use {"Field Name": value} instead of {"fieldId": value}. Send about 5,000 rows a call, fewer for a wide table: a call takes at most 100,000 items in one list and 1,000,000 values in all. A file in several calls: call it with checkOnly=true and firstRowNumber for every part first (nothing is written), then import the parts, so a wrong row anywhere imports nothing, except values that clash between parts or a limit reached by the parts together. Copying tables or rows into another base is work beyond most requests: only when the person asked for the copy.
Input Schema
tableId | stringrequired | Table ID from listTables |
rows | arrayrequired | Array of row data. When matchFieldsByName=true, use field NAMES as keys. When false, use field IDs. LINKED-RECORD columns resolve per value: sibling "<Field> (id)" column → row IDs → JSON-array string (comma-safe) → exact label → unique case-insensitive label; unresolvable values are returned in unresolvedLinks (row still imports, link skipped — never guessed, never auto-created). Relations are written both directions. |
matchFieldsByName | boolean | If true, match columns by field name. If false, use field IDs as keys (default: true) |
fieldDefinitions | array | Optional field definitions to create new fields during import |
checkOnly | boolean | Check these rows exactly as an import would, writing nothing (no rows, no columns). To import a file in several calls, check every part first, then import them: a wrong row anywhere then imports nothing, except values that clash between parts or a limit reached by the parts together. |
firstRowNumber | integer | The file's row number of rows[0] when a file goes in several calls (default 1), so an error names the row in the file. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_importRows",
"arguments": {
"tableId": "example",
"rows": {},
"matchFieldsByName": true
}
}
}tables_exportBaseExport one whole base (all its tables, fields, rows and views) as one file in the format tables_importBase takes. Param "databaseId" (from listDatabases). Returns { formatVersion, exportedAt, base, tables: [{ name, fields, rows, views }] }, the same file the Export button saves. Each row carries its id (__rowId) so an import can put linked records back; a secret column comes back blank. The file is at most 256 KB: a bigger base is refused with its size, so read it one table at a time with tables_getRows instead. For one table only use tables_exportTable. Copying tables or rows into another base is work beyond most requests: only when the person asked for the copy.
Input Schema
databaseId | stringrequired | The base to export (an id from listDatabases) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_exportBase",
"arguments": {
"databaseId": "example"
}
}
}tables_importBaseMake a new base from a file tables_exportBase gave. Params: data (required: the whole file, exactly as exported), name (optional: the new base's name; default the file's own name). It always makes a new base with all-new ids in this workspace and never changes, overwrites or deletes an existing base, table or row; a base of the same name becomes a second base. It counts against the plan: the number of bases, the rows a base may hold (all the file's tables added together), and the database storage; when a limit is reached nothing is made and the answer says which. Returns { databaseId, tablesCreated, linkedRecordsRestored, unresolvedLinks, message }; unresolvedLinks lists link cells that named a row the file does not hold (nothing is linked for them). Copying tables or rows into another base is work beyond most requests: only when the person asked for the copy.
Input Schema
name | string | Name for the new base (default: the file's own name) |
data | objectrequired | The whole file tables_exportBase gave, exactly as exported |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_importBase",
"arguments": {
"name": "example",
"data": {}
}
}
}tables_filterRowsFilter rows by field values using the SDK operator syntax (postgres-side, scales to large tables — uses a GIN index on data jsonb). Returns { rows, total, tableId, tableName, limit, offset }. Params: - tableId (required) - filter (object): keys are field IDs OR field names (case-insensitive). Multiple keys are AND-ed. Each value is a plain primitive (equality) or an operator dict. - sort: rowOrder, createdAt, updatedAt or any field name/id, optionally prefixed with "-" for descending. Numbers sort numerically, text case-insensitively, blanks last. Default: rowOrder asc. - limit: default 100, max 1000 - offset: default 0 - includeReadable: include human-readable field-name keys in output (default false; turn on only when needed). Operators: $eq, $ne, $gt, $gte, $lt, $lte, $between, $contains, $startsWith, $endsWith, $regex, $in, $nin, $not, $isEmpty, $isNotEmpty. Combine whole filters with { "$or": [...] } and { "$and": [...] }. Compare two fields of a row with $eqField/$neField/$gtField/$gteField/$ltField/$lteField. Examples: - { "Status": "todo" } → equality - { "Priority": { "$gt": 5 } } → numeric comparison - { "Title": { "$contains": "urgent" } } → substring (case-insensitive) - { "Status": { "$in": ["todo", "in-progress"] } } → multi-value - { "DueDate": { "$isNotEmpty": true } } → presence check - { "Status": "done", "Assignee": "Alice" } → AND across keys Validation: unknown field keys or unknown operators throw 400 with the list of valid options — typos surface immediately rather than returning all rows. Caveat: $gt/$gte/$lt/$lte on text fields that store numeric-looking strings ("16", "8") compare LEXICOGRAPHICALLY, so "8" > "10" is true. Use these operators only on fields whose values are stored as proper numbers, or post-filter client-side for text-stored numerics. Same operator dict as the published-app runtime SDK's entities.<Table>.filter().
Input Schema
tableId | stringrequired | Table ID to filter |
filter | object | Filter object. Keys are field IDs OR field names (case-insensitive). Multiple keys are AND-ed; { "$or": [ {...}, {...} ] } and { "$and": [...] } group full filter objects and nest arbitrarily. Each value is either a plain primitive (equality), or an operator dict. Operators: $eq, $ne, $gt, $gte, $lt, $lte, $between ([min,max] inclusive), $contains, $startsWith, $endsWith, $regex (Postgres syntax, case-sensitive — prefix (?i) for insensitive), $in, $nin (blanks count as not-in-list), $not (complement of an inner operator dict), $isEmpty, $isNotEmpty. Field-to-field: $eqField/$neField/$gtField/$gteField/$ltField/$lteField compare against ANOTHER field of the same row by name/id — { "Spent": { "$gtField": "Budget" } } finds over-budget rows. Example: { "Status": "todo", "Priority": { "$between": [5, 9] }, "$or": [{ "Tags": { "$contains": "urgent" } }, { "Due": { "$isEmpty": true } }] } Validation: unknown field keys, unknown operators, and operator/value type mismatches throw 400 with the valid options — typos surface immediately instead of returning all (or wrong) rows. Semantics: $contains is case-insensitive and matches LITERALLY (%, _ and \ are escaped); $eq is exact and case-sensitive with NO type coercion ({Score: "50"} does not match the number 50); { field: null } means "is empty"; $in: [] matches NOTHING. Field types: multiselect filters by MEMBERSHIP ($contains value, $in = any-of, plain scalar = has that tag). created_time/updated_time compare as dates ($gte/$lt ranges; ISO strings). Link fields filter by linked ROW ID via the relation table ($contains id, $in [ids], $isEmpty/$isNotEmpty). Caveat: $gt/$gte/$lt/$lte on text fields that store numeric-looking strings ("16", "8") use LEXICOGRAPHIC comparison, so "8" > "10" is true. Use these operators only on fields whose values are stored as proper numbers, or filter then sort/compare client-side for text-stored numerics. |
sort | string | Sort spec — rowOrder, createdAt, updatedAt, or ANY FIELD NAME/ID, optionally prefixed with "-" for descending (e.g. "-Total"). Defaults to rowOrder asc. Numbers sort numerically, text case-insensitively, blanks always last; scalar lookup/rollup and self-contained formulas sort server-side too. Link fields, volatile formulas and list aggregations are not sortable (clear 400). Unknown keys throw 400 — never silently ignored. |
limit | number | Max rows per page (default: 100, max: 1000) |
offset | number | Skip results for pagination |
includeReadable | boolean | Include human-readable field-name keys in output (extra cost — set true only when you need it; default false) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_filterRows",
"arguments": {
"tableId": "example",
"filter": {},
"sort": "example"
}
}
}tables_searchRowsSubstring search across all field values in a table (postgres-side, no row cap). Params: tableId (required), query (required, search string), limit (default 50, max 500), offset, includeReadable (default true). Returns matching rows with optional human-readable field names (dataReadable). For typed field-level filters, use filterRows.
Input Schema
tableId | stringrequired | Table ID to search in |
query | stringrequired | Search query string. Matches anywhere in the row's serialized data (field IDs and values). |
limit | number | Max results per page (default: 50, max: 500) |
offset | number | Skip results for pagination |
includeReadable | boolean | Include human-readable field names in output |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_searchRows",
"arguments": {
"tableId": "example",
"query": "example",
"limit": 0
}
}
}tables_upsertRowCreate or update row based on match field. Params: tableId (required), matchFieldId (required, field ID to match on), matchValue (required), data (row data keyed by field ID; exact field names are also accepted), includeReadable (default true). Returns {id, action: "created"|"updated", data, dataReadable}.
Input Schema
tableId | stringrequired | Table ID |
matchFieldId | stringrequired | Field ID to match on (must be unique or will update first match) |
matchValue | any | Value to match against |
data | objectrequired | Row data with field IDs as keys |
includeReadable | boolean | Include human-readable output |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_upsertRow",
"arguments": {
"tableId": "example",
"matchFieldId": "example",
"matchValue": {}
}
}
}tables_getTableSchemaGet table structure/schema. Params: tableId (required), format ("json"|"array"|"markdown", default "json"). Returns field definitions useful for understanding table layout before creating/updating rows.
Input Schema
tableId | stringrequired | Table ID |
format | string | Output format: json (full), array (simple), markdown (documentation) |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getTableSchema",
"arguments": {
"tableId": "example",
"format": "example"
}
}
}tables_duplicateTableClone a table with structure and optionally data. Params: tableId (source), newName (required), targetDatabaseId (optional, defaults to same database), includeRows (default false), includeViews (default true). Returns new table info. Copying tables or rows into another base is work beyond most requests: only when the person asked for the copy.
Input Schema
tableId | stringrequired | Source table ID to duplicate |
newName | stringrequired | Name for the new table |
targetDatabaseId | string | Target database ID (defaults to same database) |
includeRows | boolean | Copy all rows from source table |
includeViews | boolean | Copy views configuration |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_duplicateTable",
"arguments": {
"tableId": "example",
"newName": "example",
"targetDatabaseId": "example"
}
}
}tables_getHelpGet focused help for Tables API. Call when a parameter shape is unclear (one call per topic). Topics: - "overview": Structure, quick start, key concepts - "fields": Field types and how to create them - "rows": Creating/updating rows (uses field IDs!) - "views": View types and creation - "linked": Linking tables together Example: getHelp({topic: "rows"}) before creating rows.
Input Schema
topic | string | Topic: "overview", "fields", "rows", "views", "linked", "buttons" |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getHelp",
"arguments": {
"topic": "example"
}
}
}tables_setAccessRulesSet access rules for a table in published apps. Controls who can create/read/update/delete rows. Params: - tableId: Table ID (required) - accessRules: Object with rules per operation (create, read, update, delete) - trackCreator: boolean (default true) - Track who creates each row Each operation's value is a CONDITION: a single rule, a flat array (OR), or a boolean group. This lets you express ANY combination. Rule types (leaves): - public: Anyone (anonymous users). Always say its level: on a table it has one meaning today, written { "type": "public", "scope": "everywhere" } (on a file or flow the default is "scope": "home" with its sites: see files_setAccessRules / flow_setAccessRules). Two newer levels: "Only through this site" ({ "type": "only_through_site" }: anyone, but only from this site's own pages) and "Anyone on the internet" ({ "type": "anyone_on_internet" }: anyone, anywhere). They can be saved only once Serenities switches them on (until then a save naming one is refused and says so); from then on a public leaf that says no level is refused naming both. When a person asks for "public", ask which one they mean; never pick for them: suggest "Only through this site", and write "Anyone on the internet" only when they ask for exactly that. - authenticated: Any logged-in app user - creator: Only the user who created the row - field_match: { type: "field_match", rowField, userField, op? } - row.data[rowField] OP user.profileData[userField] (op default eq). Compared by the FIELD's type: a number field compares numbers (the profile's "5" is 5; a row holding the text "5" is not), a text field compares text, a multiselect or link field is membership. A profile value that can't be the field's type, ordering (gt/lt…) on a non-number field, or a field the table doesn't have matches no row. - user_property: { type: "user_property", field, value?, op? } - user.profileData[field] OP value - has_role: { type: "has_role", roleId: "<uuid>" } - matches users assigned this AppBuilderRole. Get role IDs from listProjectRoles. Always send roleId: a { role: "name" } is saved with that role's id beside it (so renaming the role keeps the rule), and is refused once name rules are switched off; "user" / "admin" (the built-in classification) stay names. - email: { type: "email", email: "person@example.com" } - one specific user - has_entitlement: { type: "has_entitlement", key } - members holding that paid entitlement - function: { type: "function", functionId: "<id>" } - access decided by a backend function returning true/false (NOT allowed on pages). It must be a switched-on CODE function of this site: one that is off, a data function, or fails to run can't decide: it never lets anyone in, and a rule that needs its answer (all / not / deny) refuses. It may only read, with the visitor's rights: ctx.tables reads see exactly what the table's read and field rules give that visitor, ctx.files.list/getUrl only the files that visitor may read, plus ctx.settings.get; a function rule inside it can't decide (no gate runs inside a gate) — no writes, email, posting, agents, events or AI, and no network but the bridge (no outside sites); do that work in the function or flow the rule protects - relationship: { type: "relationship", linkFieldId: "<id>", match: { via: "creator" } } - ReBAC: access via a RELATED record. Follow the link field linkFieldId on THIS table to the linked table; allow if a related row "belongs to" the user. match is one of: { via:"creator" } (related row's creator is the user), { via:"field_match", rowField, userField, op? } (related.data[rowField] OP user.profileData[userField]), or { via:"user_property", field, value?, op? }. Get link field IDs from getFieldsByTableId (fields where linkType/linkedTableId is set). Tables only — NOT allowed on pages (needs a DB join the edge can't do). Use for "vendor sees only orders for products they own", "member reads their team's projects". - policy: { "policy": "<policyId>" } - reference a reusable AccessPolicy (see listAccessPolicies/createAccessPolicy); expanded at eval. Missing/cyclic ⇒ deny. Operators (op) for user_property/field_match: eq, ne, gt, gte, lt, lte, in, nin, contains, isEmpty, isNotEmpty. Use arrays for in/nin: { type:"user_property", field:"tier", op:"in", value:["pro","enterprise"] }. Groups (combine conditions): - { "any": [condA, condB] } → OR (a flat array is shorthand for this) - { "all": [condA, condB] } → AND (every condition must pass) - { "not": cond } → NOT - { "deny": cond } → DENY-WINS: if cond matches, the WHOLE check fails, overriding every allow (e.g. "everyone paid EXCEPT banned"). Use { all: [ <allows>, { deny: { type:"user_property", field:"banned", value:true } } ] }. Groups nest arbitrarily: { all: [ { any: [tierPro, tierEnt] }, { type:"has_role", roleId } ] }. IMPORTANT: App owner always has full access. No rules / empty = deny (secure by default). A read rule saved empty ([] — "only the owner reads") also gives site functions' ctx.tables 0 rows for members and visitors. With NO read rule, a site function's ctx.tables read for a member or visitor is refused (only the owner reads); set a "creator" read rule so members read the rows they created. Field rules (accessRules.fields[<fieldId>] = { read?, write? }): same conditions. A rule that compares a record (creator, field_match, relationship) is judged on EACH row — "only the creator sees Notes" hides Notes on others' rows; a write sets the field only where the rule passes on the row the write leaves (link/unlink of a link field too). has_entitlement, function and policy work there as well. Example - Contact form (anyone submits, only owner reads): { "tableId": "xxx", "accessRules": { "create": [{"type":"public","scope":"everywhere"}], "read": [], "update": [], "delete": [] } } Example - Personal data (users see only their own): { "tableId": "xxx", "accessRules": { "create": [{"type":"authenticated"}], "read": [{"type":"creator"}], "update": [{"type":"creator"}], "delete": [{"type":"creator"}] } } Example - "(Pro OR Enterprise) AND age >= 18" can read: { "tableId": "xxx", "accessRules": { "read": { "all": [ { "any": [ {"type":"user_property","field":"tier","value":"pro"}, {"type":"user_property","field":"tier","value":"enterprise"} ] }, {"type":"user_property","field":"age","op":"gte","value":18} ] } } } Example - Vendor sees only Orders for Products they own (ReBAC, "product" = a link field on Orders → Products): { "tableId": "<ordersTableId>", "accessRules": { "read": {"type":"relationship","linkFieldId":"<productLinkFieldId>","match":{"via":"creator"}} } } Example - Paying members EXCEPT banned users (deny-wins): { "tableId": "xxx", "accessRules": { "read": { "all": [ {"type":"user_property","field":"tier","value":"pro"}, { "deny": {"type":"user_property","field":"banned","value":true} } ] } } }
Input Schema
tableId | stringrequired | Table ID from listTables |
accessRules | objectrequired | Access rules per operation (create/read/update/delete). Each is a Condition: a single rule, a legacy flat array (OR), or an { all | any | not } group. Rule types: public, authenticated, creator, field_match, user_property (with op: eq/ne/gt/gte/lt/lte/in/nin/contains/isEmpty/isNotEmpty), has_role, email, function. AND/OR/NOT groups + operators enable any combination. |
trackCreator | boolean | Whether to track who creates each row. OMIT to keep the table's current setting — it used to default to true, so a call that only changed a read rule silently re-enabled creator tracking. |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_setAccessRules",
"arguments": {
"tableId": "example",
"accessRules": {},
"trackCreator": true
}
}
}tables_getAccessRulesGet current access rules for a table. Returns rules, trackCreator setting, and help with presets. Params: tableId (required) Returns: - tableId, tableName - accessRules: Current rules per operation - trackCreator: Whether row creators are tracked - help: Rule type explanations and preset examples
Input Schema
tableId | stringrequired | Table ID from listTables |
Example Call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tables_getAccessRules",
"arguments": {
"tableId": "example"
}
}
}