Records
Canonical records after approved change requests merge.
Query Parameters
Records per page. Capped at 100; ask for the next page with cursor.
1 <= value <= 10050Restrict to one Base. OMITTING it lists records across the WHOLE SPACE, which is rarely what a caller means and is easy to miss — every other parameter still applies, so an unscoped query looks like it worked.
Opaque page cursor: pass back the nextCursor from the previous response. Do not construct or parse it — it is keyed on createdAt, or on the sort field when sort is given, and that is an implementation detail.
active is the live table; archived is the Base's trash.
"active"Value in
- "active"
- "archived"
View filters — a best-effort SUPERSET, not an exact answer. They mean what the grid means (a currency number reads as "$1,234.00", a select as its choice label), and the server may return records that do not match, so the caller must narrow them again and must NOT trust limit alongside them. Use valueFilters when you need an exact row set.
EXACT value comparisons, unlike filters which are a best-effort superset. The returned rows are exactly those that match, so limit can be trusted alongside them. Entries are ANDed; an entry may instead be { any: [...] } to OR its comparisons, which makes the list a CNF and can express any boolean combination. Requires baseId. Compares number, date, checkbox and text-like fields (text and select support eq/ne only); anything else is a 400 rather than a silently dropped condition.
Sort by one field. Only number and date fields sort authoritatively (their typed value column matches a client's own ordering); any other field type is returned in the default order and left for the caller to sort.
Response Body
application/json
curl -X GET "https://example.com/api/v1/records"{ "records": [ { "id": "string", "baseId": "string", "headCommitId": "string", "parentRecordId": "string", "parentCommitId": "string", "status": "active", "createdBy": "string", "createdByUser": null, "fieldUsers": {}, "archivedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" } } ], "nextCursor": "string"}Query Parameters
Required here, unlike records.list where omitting it spans the whole space.
1 <= lengthShow only what this saved View would: its filters and its sort.
1 <= lengthExtra conditions ANDed with the View's own — "this View, further narrowed". Unlike records.list's filters, these are EXACT: every page is precisely what the client's own matcher would keep, so a page is never missing records it should hold.
Scope the page to a date/created_time/updated_time field falling in [gte, lt) — a half-open range of absolute UTC instants, not a filters condition. Resolve the bounds yourself: a day or a month only means something in a timezone, and the server does not know the viewer's.
1-indexed, not 0-indexed.
1 <= value <= 90071992547409911Records per page. Capped at 100.
1 <= value <= 10050Response Body
application/json
curl -X GET "https://example.com/api/v1/records/page?baseId=string"{ "records": [ { "id": "string", "baseId": "string", "headCommitId": "string", "parentRecordId": "string", "parentCommitId": "string", "status": "active", "createdBy": "string", "createdByUser": null, "fieldUsers": {}, "archivedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" } } ], "total": 0, "totalPages": 0, "page": 1, "pageSize": 1}A real SQL COUNT — always the exact total, never a partial or capped number, so it's safe to render as a canonical figure (e.g. a dashboard summary tile). Plain baseId scoping is always cheap. Adding viewId and/or filters is exact too — provably-exact conditions (e.g. text equals/contains, not_empty/is_empty, checkbox is_true/is_false) stay a cheap SQL COUNT; everything else falls back to evaluating every matching row server-side, which is exact but not free on a large Base. Both viewId and filters require baseId.
Query Parameters
Restrict to one Base. Omitting it counts every record in the space. Required as soon as viewId, filters or valueFilters is given — a field slug only means something within one Base.
Count only what this saved View would display. Its filters apply; its sort is ignored, since a count has no order. Requires baseId.
Ad-hoc conditions, ANDed with the View's own when viewId is also given. Unlike records.list's superset filters, the COUNT is exact either way — but a condition whose exactness cannot be proven makes the server read every candidate row instead of running one aggregate, so prefer valueFilters where it fits. Requires baseId.
EXACT value comparisons, same shape as records.list's. Always exact, so a count scoped only by these stays a single SQL count instead of reading every candidate row. Requires baseId.
Response Body
application/json
curl -X GET "https://example.com/api/v1/records/count"{ "total": 0}One SQL GROUP BY returning every bucket's exact count — the split a board column header or a summary tile needs, without reading the records themselves. fieldSlug must name a select or checkbox field: their stored value IS the grouping key, so the buckets are exactly the ones a client would build. Grouping by a text, number or date field is rejected rather than approximated (text keys are truncated at the projection limit; date keys would bucket by the server's timezone, not the viewer's). viewId and filters narrow the set first, with the same exactness rules as records.count: provably-exact conditions stay a cheap SQL aggregate, anything else falls back to evaluating every matching row server-side — exact, but not free on a large Base. Groups come back keyed by raw choice id (or "true"/"false"), with null for records that have no value; labels are the client's to render.
Query Parameters
Required: a field slug is only unambiguous within one Base.
1 <= lengthThe field to group by. OMIT it to aggregate the whole filtered set as a single bucket, which is what a summary tile wants. Under the default grid bucketing only select and checkbox can be grouped; sql bucketing also allows number and date fields.
1 <= lengthHow records are bucketed, and the two modes disagree on real data. grid (default) buckets the way the grid renders: an unset checkbox counts as false and an empty string falls in the null bucket — right for a Kanban column header. sql buckets the way GROUP BY does: a missing value gets its OWN bucket and nothing is folded — right for anything reproducing SQL. sql also returns keys in their own type (a number for a number field) rather than as strings.
"grid"Value in
- "grid"
- "sql"
Numeric aggregates evaluated per group, keyed in the response as "<fn>:<fieldSlug>". Only number-shaped fields can be aggregated; anything else is a 400. sum/avg/min/max of a group holding no values are NULL rather than 0, and count over a FIELD counts present values — which is not the same as the group's own count, which counts records.
Group only what this saved View would display. Its filters apply; its sort is ignored.
1 <= lengthAd-hoc conditions, ANDed with the View's own when both are given. The grouping is exact either way, but a condition whose exactness cannot be proven makes the server read every candidate row instead of running one GROUP BY.
EXACT value comparisons, same shape as records.list's. Always exact, so a grouping scoped only by these stays a single SQL GROUP BY.
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/api/v1/records/group-by?baseId=string"{ "groups": [ { "value": "string", "count": 0, "aggregates": { "property1": 0, "property2": 0 } } ], "total": 0}Provide exactly one selector: recordId alone, or the complete baseId + fieldSlug + valueText tuple. Other combinations return 400.
Query Parameters
Record id selector. Use alone; do not combine with field selector fields.
1 <= lengthField selector: Base id. Requires fieldSlug and valueText.
Field selector: exact field slug. Requires baseId and valueText.
1 <= lengthField selector: exact text value. Requires baseId and fieldSlug.
1 <= lengthResponse Body
application/json
application/json
application/json
curl -X GET "https://example.com/api/v1/records/get"{ "id": "string", "baseId": "string", "headCommitId": "string", "parentRecordId": "string", "parentCommitId": "string", "status": "active", "createdBy": "string", "createdByUser": null, "fieldUsers": {}, "archivedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" }}Query Parameters
Restrict to one Base. Omitting it searches the whole space.
The field's SLUG, not its display name — visible in the Base's field settings.
1 <= lengthMatched by EXACT equality, not substring or fuzzy — this is the de-dup-by-key lookup. Use /api/v1/search for full-text.
1 <= lengthMaximum matches to return. Capped at 100.
1 <= value <= 10050Response Body
application/json
curl -X GET "https://example.com/api/v1/records/search?fieldSlug=string&valueText=string"[ { "id": "string", "baseId": "string", "headCommitId": "string", "parentRecordId": "string", "parentCommitId": "string", "status": "active", "createdBy": "string", "createdByUser": null, "fieldUsers": {}, "archivedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" } }]curl -X GET "https://example.com/api/v1/records/string/change-requests"[ { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "status": "in_review", "submittedBy": "string", "submittedByUser": null, "sourceAttribution": { "displayName": "string", "ownerName": "string", "channel": "web_ui" }, "sourceMeta": { "property1": null, "property2": null }, "reviewPolicySnapshot": { "property1": null, "property2": null }, "mergeSummary": { "property1": null, "property2": null }, "rejectedReason": "string", "reviewedAt": "string", "mergedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "node": { "id": "string", "parentId": "string", "type": "folder", "slug": "string", "name": "string", "description": "string", "metadata": {}, "settings": {}, "explicitVisibility": null, "icon": null, "position": 0, "createdAt": "string", "updatedAt": "string", "baseId": "string", "children": [ { "id": "string", "parentId": "string", "type": "folder", "slug": "string", "name": "string", "description": "string", "metadata": {}, "settings": {}, "explicitVisibility": null, "icon": null, "position": 0, "createdAt": "string", "updatedAt": "string", "baseId": "string", "children": [ null ], "hasChildren": true, "shared": true } ], "hasChildren": true, "shared": true }, "operations": [ { "id": "string", "changeRequestId": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operation": "node_create", "status": "pending", "targetRecordId": "string", "targetViewId": "string", "filePath": "string", "sourceRecordId": "string", "sourceCommitId": "string", "baseCommitId": "string", "headCommitId": "string", "deleteMode": "archive", "mergedRecordId": "string", "mergedViewId": "string", "position": 0, "createdAt": "string", "updatedAt": "string", "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" }, "baseFields": { "property1": null, "property2": null } } ], "primaryOperation": { "id": "string", "changeRequestId": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operation": "node_create", "status": "pending", "targetRecordId": "string", "targetViewId": "string", "filePath": "string", "sourceRecordId": "string", "sourceCommitId": "string", "baseCommitId": "string", "headCommitId": "string", "deleteMode": "archive", "mergedRecordId": "string", "mergedViewId": "string", "position": 0, "createdAt": "string", "updatedAt": "string", "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" }, "baseFields": { "property1": null, "property2": null } }, "operationCount": 0, "reviews": [ { "id": "string", "changeRequestId": "string", "reviewerId": "string", "reviewer": null, "verdict": "approved", "reason": "string", "visibleOperationHeads": { "property1": "string", "property2": "string" }, "createdAt": "string" } ] }]Path Parameters
1 <= lengthRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
curl -X POST "https://example.com/api/v1/records/string/change-requests" \ -H "Content-Type: application/json" \ -d '{ "operation": "update" }'{ "id": "string", "baseId": "string", "headCommitId": "string", "parentRecordId": "string", "parentCommitId": "string", "status": "active", "createdBy": "string", "createdByUser": null, "fieldUsers": {}, "archivedAt": "string", "createdAt": "string", "updatedAt": "string", "base": { "id": "string", "nodeId": "string", "slug": "string", "name": "string", "description": "string", "reviewPolicy": { "kind": "single", "requiredApprovals": 0 }, "createdAt": "string", "fields": [ { "id": "string", "baseId": "string", "slug": "string", "name": "string", "type": "text", "required": true, "position": 0, "options": {} } ] }, "headCommit": { "id": "string", "baseId": "string", "targetType": "base", "nodeId": "string", "operationId": "string", "parentCommitId": "string", "payload": { "property1": null, "property2": null }, "operation": "node_create", "message": "string", "author": "string", "authorUser": null, "createdAt": "string" }, "materialized": true}curl -X GET "https://example.com/api/v1/records/string/links"[ { "id": "string", "baseId": "string", "fieldId": "string", "fieldSlug": "string", "sourceRecordId": "string", "targetBaseId": "string", "targetRecordId": "string", "commitId": "string", "position": 0, "createdAt": "string", "updatedAt": "string" }]