Tables, records & fields
The Tables module is the core data substrate of Onnie. The API treats it the same way you do in the UI, with a few JSON-shape rules to keep in mind.
Tables
A table has a UUID, a name, and a list of typed columns. Columns can be: TEXT, LONGTEXT, EMAIL, PHONE, URL, NUMBER, BOOLEAN, DATETIME, SELECT, MULTISELECT, REFERENCE, FILE, FORMULA, AI, SERIAL.
FORMULA, AI, and SERIAL are computed — the API returns their values but never accepts them on create or update.
FILE is read-only in v1 — the API returns existing file metadata but doesn't accept file uploads.
Records
A record is a row in a table. The API represents a record as:
{
"id": "uuid",
"fields": { "<column name>": "<value>" },
"created_at": "2026-05-22T10:00:00.000Z",
"updated_at": "2026-05-22T10:00:00.000Z"
}
fields is keyed by column name, not column ID — friendlier for humans and for agents reading the spec. The per-table OpenAPI document gives you the exact field shapes for any table you have access to.
Field types in JSON
| Column type | JSON shape |
|---|---|
TEXT, LONGTEXT, EMAIL, PHONE, URL | string |
NUMBER | number |
BOOLEAN | boolean |
DATETIME | ISO-8601 string |
SELECT | string (must match an option name) |
MULTISELECT | array of strings |
REFERENCE | UUID string, or array of UUIDs if multiple |
FILE, FORMULA, AI, SERIAL | read-only — present on responses, rejected on writes |
Updates are atomic merges
PATCH /tables/{tableId}/records/{id} is an atomic merge: send only the fields you want to change. Server-side, the merge happens in a single database statement — no read-modify-write race conditions.