| title | Data API |
|---|---|
| description | REST endpoints for CRUD, batch operations, record cloning, and analytics queries. |
Record CRUD, batch operations, and analytics queries over REST. All paths are relative to the base URL (defaults to /api/v1) — see the API Overview for discovery and service availability.
CRUD operations on any object. Always available — provided by the kernel.
Query records with filtering, sorting, selection, and pagination.
| Parameter | Location | Description |
|---|---|---|
object |
path | Object name |
select |
query | Comma-separated field names |
filter |
query | Filter expression (JSON). filters also accepted for backward compatibility. |
sort |
query | Sort expression (e.g. name asc or -created_at) |
top |
query | Max records to return. No default — omitting it returns all matching records. |
skip |
query | Offset |
expand |
query | Comma-separated list of relations to eager-load |
search |
query | Full-text search query |
Note: OData-style
$-prefixed parameters ($filter,$select,$orderby,$top,$skip,$expand,$count,$search) are also accepted directly on this same endpoint as aliases — they're normalized internally to the parameter names above. There is no separate standalone OData endpoint.
Response:
{
"object": "account",
"records": [{ "id": "1", "name": "Acme Corp", ... }],
"total": 42,
"hasMore": true
}Get a single record by ID. Only select and expand query parameters are allowed; all other parameters are discarded.
| Parameter | Location | Description |
|---|---|---|
object |
path | Object name |
id |
path | Record ID |
select |
query | Comma-separated field names to include |
expand |
query | Comma-separated list of relations to eager-load |
Response: { object: "account", id: "1", record: { ... } }
Create a new record.
Body: { name: "Acme Corp", industry: "Technology" }
Response: { object: "account", id: "1", record: { ... } }
Update an existing record (partial update).
Body: { industry: "Healthcare" }
Response: { object: "account", id: "1", record: { ... } }
Delete a record.
Response: { object: "account", id: "1", success: true }
Efficient bulk operations. Always available.
Execute a batch operation (create / update / upsert / delete) on multiple records.
Body:
{
"operation": "update",
"records": [
{ "id": "1", "data": { "status": "active" } },
{ "id": "2", "data": { "status": "active" } }
],
"options": {
"atomic": true,
"returnRecords": true,
"continueOnError": false,
"validateOnly": false
}
}Response: BatchUpdateResponse with succeeded, failed, total, and a per-record results array. Each entry in results has id, success, an optional errors array, and optional data (the full record, present when returnRecords is true).
Batch create multiple records.
Body: a bare array of records — [{ name: "A" }, { name: "B" }]. The REST handler reads the request body directly as the records array, so do not wrap it in { records: [...] }.
Response: { object: "account", records: [...], count: 2 }
Batch update multiple records.
Body:
{
"records": [
{ "id": "1", "data": { "status": "active" } },
{ "id": "2", "data": { "status": "closed" } }
],
"options": { "atomic": false }
}Batch delete records by ID list.
Body: { ids: ["1", "2", "3"] }
Clone a record. Reads the source, drops engine-owned columns (id, the audit
fields, autonumbers, and computed formula/summary values) so they are
re-derived, applies any caller overrides, and inserts the copy. Shallow by
design — it duplicates the record's own fields, not its child records.
Gated by the object's enable.clone capability (default true); an object with
enable.clone: false returns 403 CLONE_DISABLED.
Body (optional): { "overrides": { "name": "Acme (Copy)" } } — applied on
top of the copied values (a bare field map is also accepted). The natural place
to set a new name or clear a unique field.
Response 201: { object, id, sourceId, record }
Semantic BI queries using a cube-style API. Available when the analytics service is enabled.
Execute an analytics query.
Body:
{
"cube": "account",
"measures": ["revenue.sum", "count"],
"dimensions": ["industry"],
"where": { "status": "active" },
"limit": 100
}Response: the runtime dispatcher wraps the AnalyticsResult as { success: true, data: { rows, fields, sql?, totals? } }:
{
"success": true,
"data": {
"rows": [
{ "industry": "Technology", "revenue.sum": 150000, "count": 5 },
{ "industry": "Healthcare", "revenue.sum": 80000, "count": 3 }
],
"fields": [
{ "name": "industry", "type": "string", "label": "Industry" },
{ "name": "revenue.sum", "type": "number", "label": "Revenue Sum", "format": "$0,0" },
{ "name": "count", "type": "number", "label": "Count" }
],
"sql": "SELECT ..."
}
}Get metadata for all registered cubes. Cubes are explicitly defined (via defineCube
or the analytics service's cubes config) — a cube referenced by a query that isn't
yet registered is lazily auto-inferred from that query's shape, but metadata isn't
proactively generated for every object.
Pass ?cube=<name> to filter the listing to a single cube (this is what
client.analytics.meta(cube) sends).
Response: { success: true, data: [...] } where data is an array of cube
definitions with measures and dimensions (time-based dimensions are dimensions
entries with type: "time").
Generate the SQL for a given analytics query without executing it (dry-run/debug).
Accepts the same body shape as /analytics/query; support depends on the underlying
driver/strategy.
Response: { success: true, data: { sql: string, params: unknown[] } }
- API Overview — discovery, error handling, and protocol types
- Metadata & Package API — schemas, views, and package management