Tables
Tables API
Custom data tables allow you to store and manage structured data that agents can query and manipulate.
Table Management
List Tables
GET /orgs/{organizationId}/tablesQuery Parameters:
limit(optional): Number of results (default: 20)offset(optional): Pagination offset (default: 0)
Response:
{
"items": [
{
"name": "customers",
"organizationId": "org-123",
"description": "Customer information",
"fields": [
{
"name": "name",
"type": "text",
"required": true,
"comments": "Customer full name"
},
{
"name": "email",
"type": "text",
"required": true,
"comments": "Email address"
},
{
"name": "is_active",
"type": "boolean",
"required": false,
"default": true,
"comments": "Account status"
},
{
"name": "metadata",
"type": "jsonb",
"required": false,
"comments": "Additional data"
}
],
"creatorUserId": "user-456",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
],
"totalRows": 1,
"offset": 0
}Create Table
POST /orgs/{organizationId}/tablesRequired Role: Admin or Owner
Required Fields:
name: Table name (must be unique). Only letters, numbers, and underscores are allowed.fields: Array of field schemas (at least one).
Optional Fields:
description: Table description
id. id is a reserved column name — do not define a field called id (the request is rejected) and do not supply id in row data.Request Body:
{
"name": "products",
"description": "Product catalog",
"fields": [
{
"name": "name",
"type": "text",
"required": true,
"comments": "Product name"
},
{
"name": "price",
"type": "numeric",
"required": false,
"default": 0,
"comments": "Price in USD"
},
{
"name": "in_stock",
"type": "boolean",
"required": false,
"default": true,
"comments": "Availability status"
},
{
"name": "metadata",
"type": "jsonb",
"required": false,
"comments": "Product metadata"
},
{
"name": "created_at",
"type": "timestamp",
"required": false,
"comments": "Creation timestamp"
}
]
}Field Types:
text- Text/string datanumeric- Numbers (integers and decimals)boolean- True/false valuestimestamp- Date and time valuesjsonb- JSON objects (stored as PostgreSQL JSONB)
Field Schema:
name(required): Field name — only letters, numbers, and underscores are allowedtype(required): Field typerequired(required): Whether field is required (boolean)default(optional): Default value if not providedcomments(required): Description/comment for the field
Response: Returns created CustomTable object
Get Table
GET /orgs/{organizationId}/tables/{tableName}Returns table metadata and field schemas.
Response: Returns CustomTable object
Delete Table
DELETE /orgs/{organizationId}/tables/{tableName}Required Role: Admin or Owner
Permanently deletes the table and all its data.
Response:
{
"organizationId": "org-123",
"tableName": "products"
}Table Data Operations
List Rows
GET /orgs/{organizationId}/tables/{tableName}/dataQuery Parameters:
limit(optional): Number of rows (default: 20)offset(optional): Pagination offset (default: 0)orderBy(optional): Sort specification (format:field1:asc,field2:desc)filters(optional): Filter specification. Each segment isfield:operator:value:join— the trailingjoin(and/or) is required on every segment, including a single filter. Segments are separated by|||(recommended) or,(legacy). Prefer|||because a comma separator collides with comma-separatedin/ninvalues.
Example with Filters and Sorting:
GET /orgs/org-123/tables/products/data?limit=10&offset=0&orderBy=price:desc&filters=in_stock:eq:true:and|||price:gte:100:andFilter Operators:
eq- Equal tone- Not equal togt- Greater thanlt- Less thangte- Greater than or equal tolte- Less than or equal tolike- Case-sensitive pattern matchingilike- Case-insensitive pattern matchingin- In list (comma-separated values)nin- Not in list
like/ilike, the value is wrapped in %…% automatically (spaces become %), so pass the raw substring — do not add your own % wildcards.Filter Join (applied between this segment and the previous one, left-to-right with no bracketing; the join on the first segment is ignored):
and- AND with the preceding conditionor- OR with the preceding condition
Response:
{
"items": [
{
"id": 1,
"name": "Premium Widget",
"price": 299.99,
"in_stock": true,
"metadata": {
"category": "electronics",
"tags": ["featured", "premium"]
},
"created_at": "2024-01-01T00:00:00Z"
}
],
"totalRows": 45,
"offset": 0
}Create Row
POST /orgs/{organizationId}/tables/{tableName}/dataRequired Role: Admin or Owner
Request Body:
{
"data": {
"name": "New Product",
"price": 49.99,
"in_stock": true,
"metadata": {
"category": "accessories"
}
}
}Do not include id — it is auto-generated and rejected if supplied. Any key that is not a defined field is rejected with a 400.
Response: Returns the created row (including its generated id)
Update Row
PUT /orgs/{organizationId}/tables/{tableName}/data/{id}Required Role: Admin or Owner
The {id} parameter is the numeric row ID (auto-generated integer).
Request Body:
{
"data": {
"price": 39.99,
"in_stock": false
}
}Only include fields you want to update. Other fields remain unchanged.
Response: Returns the updated row
Delete Row
DELETE /orgs/{organizationId}/tables/{tableName}/data/{id}Required Role: Admin or Owner
Permanently deletes the row.
Response:
{
"organizationId": "org-123",
"tableName": "products",
"id": 101
}Bulk Operations
Batch Insert
POST /orgs/{organizationId}/tables/{tableName}/data/batchRequired Role: Admin or Owner
Insert multiple rows in a single request.
Request Body:
{
"data": [
{
"name": "Product 1",
"price": 10.00,
"in_stock": true
},
{
"name": "Product 2",
"price": 20.00,
"in_stock": true
},
{
"name": "Product 3",
"price": 30.00,
"in_stock": false
}
]
}Rows are validated individually; invalid rows are skipped and reported in errors, while valid rows are still inserted. ids contains the generated integer IDs of the inserted rows.
Response:
{
"inserted": 3,
"failed": 0,
"errors": [],
"ids": [1, 2, 3]
}If validation fails for some rows:
{
"inserted": 2,
"failed": 1,
"errors": [
{
"index": 1,
"error": "Invalid record"
}
],
"ids": [1, 3]
}Column Operations
Rename Column
PUT /orgs/{organizationId}/tables/{tableName}/columns/{columnName}Required Role: Admin or Owner
Request Body:
{
"newName": "unit_price"
}newName must be alphanumeric/underscore only and must not be a reserved name (id).
Response: Returns the updated CustomTable object.
Link Column to the Data Dictionary
POST /orgs/{organizationId}/tables/{tableName}/columns/{columnName}/dd-linkRequired Role: Admin or Owner
Links (or re-links) a column to a Data Dictionary field. The column’s physical name must equal ddFieldName (rename the column first if it differs).
Request Body:
{
"ddFieldName": "sale_price"
}ddFieldName must be lowercase letters, numbers, and underscores.
Response: Returns the updated CustomTable object.
Unlink Column from the Data Dictionary
DELETE /orgs/{organizationId}/tables/{tableName}/columns/{columnName}/dd-linkRequired Role: Admin or Owner
Strips the Data Dictionary link while preserving the column and its data. Idempotent — unlinking a column that is not linked returns the table unchanged.
Response: Returns the updated CustomTable object.
Using Tables with Agents
Agents can query and manipulate tables using the database tool.
Example Agent Configuration:
{
"name": "Inventory Assistant",
"tools": [
{
"toolCode": "database",
"configuration": {
"tables": ["products", "inventory"]
}
}
]
}The agent can then:
- “Show me all products under $100”
- “Update the price of product 123 to $79.99”
- “Add a new product called ‘Widget Pro’ with price $150”
- “How many products are currently in stock?”
Filter Examples
Every segment carries a trailing join (and/or), even a single filter.
Simple Equality
filters=in_stock:eq:true:andMultiple Conditions (AND)
filters=in_stock:eq:true:and|||price:gte:100:andMultiple Conditions (OR)
filters=category:eq:electronics:and|||category:eq:accessories:orThe join on the second segment (or) is what combines the two conditions.
Pattern Matching
filters=name:ilike:widget:andMatches any product name containing “widget” (case-insensitive). The value is wrapped in %…% automatically.
Numeric Ranges
filters=price:gte:50:and|||price:lte:200:andMatches prices between $50 and $200
Complex Example
filters=in_stock:eq:true:and|||price:gte:100:and|||category:in:electronics,accessories:andNote the ||| separator between segments — an in value list uses commas internally, so the comma separator cannot be used here.
Sorting Examples
Single Field Ascending
orderBy=price:ascSingle Field Descending
orderBy=created_at:descMultiple Fields
orderBy=in_stock:desc,price:ascFirst by in_stock (descending), then by price (ascending)
Best Practices
- Table Naming - Use lowercase snake_case for table names (e.g.,
customer_orders) - Field Naming - Use lowercase snake_case for field names (e.g.,
created_at) - Required Fields - Mark critical fields as required
- Default Values - Provide sensible defaults when possible
- Comments - Always provide descriptive comments for fields
- JSONB for Flexibility - Use
jsonbtype for dynamic/nested data - Batch Operations - Use batch insert for importing large datasets
- Indexing - Tables are automatically indexed on the auto-generated
id - Data Validation - All data is validated against field schemas before insert
Limitations
- No Field Changes: Cannot add, remove, or re-type fields after creation (you can rename a column and link/unlink DD fields)
- Write Operations Are Admin/Owner Only: Reads are available to the Chat role
- Row ID: Each row has an auto-generated numeric
id(reserved, not customizable, never supplied on write) - No Joins: Each table is independent (no foreign key relationships)
- No Computed Fields: All fields must be explicitly provided
Common Issues
| Issue | Solution |
|---|---|
| Table creation fails | Check table name is unique and lowercase |
| Insert fails validation | Verify all required fields are provided |
| Filter not working | Check field name spelling and operator syntax |
| Cannot change fields | Field set is fixed - rename columns or create a new table |
| Permission denied | Ensure user has Admin or Owner role |
| Row not found | Verify row ID exists in table |
Data Types Details
text
Stores string data. No length limit.
numeric
Stores numbers. Can be integers or decimals.
{ "price": 99.99 }
{ "quantity": 5 }boolean
Stores true or false.
{ "is_active": true }timestamp
Stores ISO 8601 formatted timestamps.
{ "created_at": "2024-01-01T12:00:00Z" }jsonb
Stores JSON objects. Supports nested structures.
{
"metadata": {
"tags": ["featured", "sale"],
"attributes": {
"color": "blue",
"size": "large"
}
}
}