Custom tables
Shipped
Beyond the records mirrored from your connected sources, Turbobase lets you author your own native tables — tables with no external source, defined from scratch — that live right alongside your canonical data and get the same treatment: row-level security, realtime, the AI catalog, and a write path.
Authoring a table
Section titled “Authoring a table”Administrators create a table from the Records page at /data/sync/records — the “New custom table” builder names the table and defines its fields — or by asking Pilot, your AI assistant, to create one in chat. Today’s field types are text, number, date, boolean, and a free-form select. The table is created as a cr_ (record) or cl_ (list) table; because there’s no external source to review against, a table you author is auto-approved on creation — you are the authority for it.
Definitions are validated up front: a reserved, duplicate, empty, or invalid table or field name is rejected before any change is made, so a bad definition can never leave a half-created table. Custom fields on these tables promote into cf_sa_* columns, reusing the same promotion machinery as source-discovered custom fields.
A table is never finished: use + New field on its row in the Records page to add a field later, and existing rows keep their data.
From the CLI, or an app’s manifest
Section titled “From the CLI, or an app’s manifest”The builder is not the only door. The same tables can be authored from the CLI, which is how an app developer or an AI agent works:
turbobase tables list # what exists, and who claims itturbobase tables create tasks -f title:text -f due:date # one table, right nowturbobase tables plan # preview; writes nothingturbobase tables apply # apply this project's declarationsAn app can also declare the tables it needs in its own public/turbobase.json, so deploying the app creates them:
{ "tables": [ { "slug": "task", "displayName": "Tasks", "fields": [ { "name": "title", "type": "text", "required": true }, { "name": "due", "type": "date" } ] } ]}Writing to a table you authored
Section titled “Writing to a table you authored”A write goes through the same path as any mirrored record — POST /v1/data/{recordType}, or the app client’s write helper — and the record type is the table’s slug (task) or the customrecord_-prefixed spelling of it (customrecord_task). Both name the same rows, so an app can use either — and both wait for the same thing: a table is writable once its declaration has been reconciled, so a write made before that, or after a reconcile that failed on it, is refused record type "…" is not writable rather than attempted against a table that may be half-built.
Either way, a row in a table you authored stays on the instance. Turbobase reads which system owns the table off the definition, not off the system the signed-in session happens to be connected through, so the row is marked as belonging to the instance and is never queued out to a connected source — there is nothing upstream to send it to. A write that names a connected source anyway, on a table that source does not have, is refused as it is made and says so, instead of being accepted and failing against that source hours later.
When two apps want the same table
Section titled “When two apps want the same table”Several apps on one instance can legitimately want a task table, so a declaration is a claim, not an instruction. The table you get is the merge of every claim on it: a field appears if any app asked for it, and two apps asking for the same field is agreement rather than a collision.
The one case that fails is two claimants declaring the same field name at genuinely different types — one app’s due as a date and another’s as text. A column can only have one type, and silently picking one would break the other app’s reads, so the deploy is refused with a report naming both apps and both types. Rename your field, or use the type that is already there. turbobase tables plan reports the same thing without changing anything, so a conflict is discoverable before it is a failed deploy.
The Records page shows Claimed by on every shared table, so you can see an app depends on one before you drop it.
Releasing a claim — an app that stops declaring a field, or is deleted — shrinks the definition but never drops a column or its data. Your data outlives the app that introduced it; only an explicit, confirm-gated turbobase tables drop removes anything.
Permissions
Section titled “Permissions”You grant per-role read/write/delete on a custom table and can toggle a row-level “restrict to owner” flag, so the same capability and scope model that protects your canonical data also protects the tables you author.
Related
Section titled “Related”- Custom fields — promote source-discovered fields into columns
- MCP server — custom tables are discoverable and addressable over MCP
- Data model