Skip to content
BuildBase

Records API

Read records for a collection by slug, write them with the version's ids, and delete them by id.

Records are read by collection slug, not id - which makes the read path stable across environments where ids differ.

curl -G https://api.console.buildbase.app/api/collections/data/contact-requests \
  -H "Authorization: $BUILDBASE_TOKEN" \
  --data-urlencode 'latest=true'

Returns every record for the resolved version, unpaginated.

Warning

version is required unless latest=true. Without either, the request returns 400. No other query parameters are accepted, including the usual $page and $limit.

Choosing a version

QueryReads
latest=trueThe live, published version, whatever its number
version=3Version number 3 specifically, live or not

latest=true is what you want in an application - it follows the published version, so creating a draft version in the console does not change what your app reads until you publish it. version is ignored when latest=true. Pin a number when you are migrating and need the old shape.

Records are stored against the version they were written to. After you promote a new version, latest=true returns only the records stored against it; records written under an older version are still there, and you read them with version=<n>.

Failures are 404:

ConditionResponse
No collection with that slug{"error": true, "message": "not found collection:<slug>"}
No version with that number{"error": true, "message": "not found version:<n>"}
latest=true, nothing live{"error": true, "message": "not found version:live"}

The response

An array of record documents. Each carries the submitted values under data, keyed by field slug, plus the version it was written against:

[
  {
    "_id": "665f…",
    "collectionId": "664a…",
    "versionId": "664b…",
    "formId": "664c…",
    "data": { "email": "[email protected]", "message": "Interested" },
    "createdAt": "2026-08-15T09:12:44.108Z"
  }
]

formId is present only on records created through a form submission.

Warning

This endpoint returns every record for the version with no pagination. On a collection behind a busy public form that grows without bound. Read it from a background job rather than a request handler, and to page, use the paginated list, GET /api/collections/:collectionId/versions/:versionId/records.

Writing records

Records are written under their version, by id:

MethodPathPurpose
GET/api/collections/:collectionId/versions/:versionId/recordsList, paginated, with filter
GET/api/collections/:collectionId/versions/:versionId/records/:recordIdOne record
POST/api/collections/:collectionId/versions/:versionId/recordsCreate a record
PATCH/api/collections/:collectionId/versions/:versionId/records/:recordIdUpdate a record
DELETE/api/collections/:collectionId/versions/:versionId/records/:recordIdDelete a record

The body is the record's data, keyed by field slug:

curl -X POST \
  https://api.console.buildbase.app/api/collections/$COLLECTION_ID/versions/$VERSION_ID/records \
  -H "Authorization: Bearer $BUILDBASE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Release 23", "version": "1.0.0", "published": true }'

A key that is not one of the version's field slugs is dropped. A file field takes asset ids, as the overview describes.

How values are stored

Each value is checked against its field's type and stored by that type, not by how it looks. The rules are the ones a form submission follows:

Field typeTakesStored
text, rich-text, colora stringthe same string: "5550100" stays text
email, linka string in that formatthe same string
numbera number, or a string that is one ("42")a number
booltrue, false, "true" or "false"true or false
datea string or a number that reads as a dateas sent
filean asset id, { "assetId": "..." }, or a list of themthe asset

POST checks every field, and a required field needs a value: a string of spaces does not count. PATCH checks only the fields it sends and keeps the others as stored, so a required field is refused only when it is sent empty. A null value counts as not sent, and an empty string clears an optional field.

A write with any problem is refused with 400 and nothing is stored. errors lists every problem at once, and message holds them all:

{
  "error": true,
  "message": "Name must be text. Age must be a number",
  "errors": ["Name must be text", "Age must be a number"]
}

A file larger than its field's maxMb is one of those problems, and its sentence states the limit and the file's size in bytes. The answer then also carries maxBytes, the limit of the first such file, and tooLarge, one entry per file over its field's limit with field, maxBytes and size:

{
  "error": true,
  "message": "Photo: Files can be up to 5 MB (5,242,880 bytes) here. This one is 5,242,881 bytes",
  "errors": [
    "Photo: Files can be up to 5 MB (5,242,880 bytes) here. This one is 5,242,881 bytes"
  ],
  "maxBytes": 5242880,
  "tooLarge": [{ "field": "photo", "maxBytes": 5242880, "size": 5242881 }]
}

It stays a 400, not the 413 an upload answers: the file is already stored, and the fix is a smaller file or a larger maxMb on the field.

Records written before these checks may hold a number in a text field, such as 5550100 for a phone number. If you have older records, read text fields with String(value).

Deleting a record

curl -X DELETE \
  https://api.console.buildbase.app/api/collections/$COLLECTION_ID/versions/$VERSION_ID/records/$RECORD_ID \
  -H "Authorization: $BUILDBASE_TOKEN"

Records are nested under their version, so deletion needs all three ids. There is no bulk delete.

Deletion is decided by role, not by who created the record. Any role with delete permission on collection records can delete any record in the org, including records created by a public form submission, which have no creator.

Value types

Whichever way a record arrives - the console, the API, or a public form - its values are stored by their fields' types (how values are stored). A form submitted with FormData sends "12" to a number field and "true" to a bool field; they are stored as 12 and true. See custom forms.

From the server SDK

Read a collection's records from your own server, for example to render a page. createServerClient from @buildbase/sdk/server reads the same records, with caching built in, on Node 18+, Bun, Deno, Cloudflare Workers and edge runtimes. It refuses to run in a browser, so the token stays on your server.

lib/releases.ts
import { createServerClient } from '@buildbase/sdk/server';

const content = createServerClient({
  serverUrl: 'https://api.console.buildbase.app',
  apiToken: process.env.BUILDBASE_API_TOKEN!, // <orgId>:<secret>
});

// The live version; pass { version: 3 } to pin one
export const releases = await content.collections.data<{
  title: string;
  version: string;
}>('release-notes');

Reads are cached. A fresh entry (60 seconds by default) costs no request. After that, a read asks the server whether anything changed (with If-None-Match), so an unchanged collection costs one short 304 Not Modified answer. Identical reads made at the same moment share one request. content.collections.list() lists the collections themselves.

When records change, a content webhook clears the cached read: collection.record_created, collection.record_updated, collection.record_deleted or collection.version_published. And npx buildbase content pull writes every live collection to a JSON file at build time. See Delivering content for the cache stores, the webhook handler and the build-time pull. All of it is in the published @buildbase/sdk, 1.3.0.

Next Steps