Tables & fields
Everything your workspace tracks lives in tables you design yourself: a table is a named collection of records, and its fields define what each record holds — a deal’s amount, a contact’s email, a job’s completion date. Tessule validates every record against your field definitions, so the schema is not just labels on columns: it is the contract your data is held to.
If you only remember one thing: the schema is live. Every change you make is checked against the records already in the table — a change that would invalidate existing data is refused with examples of the offending rows, rather than silently corrupting or discarding anything. You can always loosen a rule; tightening one has to be earned by the data.
Who can do what
Section titled “Who can do what”Schema management is governed by the standard actions and roles model:
- See tables and their schema —
tables:read, held by every built-in role including Reader. - Create, rename, and delete tables —
tables:manage, held by Member and Admin by default. - Add, edit, and delete fields (including table constraints and stage
designation) —
fields:manage, held by Member and Admin by default. - Change field visibility and grant roles access to private fields —
permissions:manage, Admin only. - Apply AI-generated schema plans —
schema:apply, Admin only.
Note that ordinary members can reshape the schema by default. If that is too
permissive for your team, demote them to Reader and grant the rest of the
Member abilities back through a custom role that omits tables:manage and
fields:manage.
Where you do this in the app
Section titled “Where you do this in the app”- Data (sidebar) — the overview page: a relationship diagram of your tables (references drawn as arrows, system entities in purple) and the AI schema assistant chat.
- Table Administration (sidebar) — the table list, where you create, edit, and delete tables.
- The schema editor — from Table Administration, open a table’s schema page. This is where fields are added and edited: a grid showing each field’s name, display name, type, Required/Unique chips, the Visibility chip, default value, and reference target. It also hosts the record-layout editor and the stage-designation dialog (see Lifecycles).
Tables
Section titled “Tables”Creating a table
Section titled “Creating a table”Create a table from Table Administration → Create Table
(POST /tables). A table has:
| Setting | Meaning |
|---|---|
| Display name | What people see everywhere in the app. Freely editable later. |
| Name | The internal identifier (lowercase, underscores), auto-derived from the display name. Fixed at creation — formulas, queries and the API address the table by this name, so it never changes. |
| Description | Free-text notes shown with the table. |
| AI hint | Guidance for AI assistants about the table’s purpose and relationships. |
| Record visibility | workspace (default — everyone with record access sees all rows) or private (rows are visible per owner/scope/share). See Authorization. |
| Comments | Whether records carry a discussion thread. Off by default; turning it off later hides existing comments but never deletes them. |
| Searchable | Whether the table participates in global search. On by default; turning it off drops the table’s search index entirely. API-only — the create/edit dialog has no toggle for it yet. |
Renaming and settings
Section titled “Renaming and settings”Edit a table from the table list (PUT /tables/{tableId}). You can change
the display name, description, AI hint, record visibility, comments, and
searchability — but not the internal name.
Deleting a table
Section titled “Deleting a table”Deleting a table (DELETE /tables/{tableId}) removes it and all of its
records from the workspace. There is no undo from the app — treat it as
permanent. One special case: a SQL view that other views are built on
cannot be deleted until those dependent views are dropped first (the request
fails rather than cascading). SQL views themselves are read-only derived
tables and have their own guide: Views.
A small example schema
Section titled “A small example schema”The field-type catalogue
Section titled “The field-type catalogue”| Category | Type | Holds | Notes |
|---|---|---|---|
| Text | text |
Short text | Up to 255 characters by default |
textarea |
Long text | Up to 32,000 characters by default | |
richtext |
Formatted text (HTML) | Up to 128 KB by default | |
| Number | integer |
Whole numbers | |
number |
Decimals | ||
currency |
An amount plus a currency code | Stored as a pair, e.g. 100 USD | |
percent |
A percentage | 0–100 by default | |
| Choice | boolean |
Yes/no | Labels configurable |
picklist |
One value from a fixed option list | The lifecycle workhorse — see Lifecycles | |
multipicklist |
Several values from a fixed option list | ||
| Date & time | date |
A calendar date | Default can be Today (the day each record is created) |
datetime |
A timestamp | Default can be Now (the moment each record is created) | |
time |
A time of day | ||
| Contact | email |
An email address | Format-validated |
phone |
A phone number | ||
url |
A web address | Format-validated | |
| Relationship | reference |
A link to one record in another table (or a user/group) | See “Relationships” below |
| Compound | address |
Street/city/state/postal code/country | |
geolocation |
Latitude + longitude | ||
openingHours |
Per-day open/close times | ||
| Files | attachment |
Uploaded files | |
| Computed | formula |
A value calculated from the record’s other fields | Read-only; see Formulas, rollups & summaries |
lookup |
A value projected from the referenced record | Read-only, always current | |
rollup |
An aggregate over records that reference this one | Read-only, always current | |
autonumber |
A system-assigned, gapless document number | Read-only; format like INV-{0000} |
Computed types are read-only on the write path — the server assigns or calculates their values, and any attempt to write them is rejected. The formula language, lookups, rollups, and the sheet’s computed columns are covered in depth in Formulas, rollups & summaries.
Configuration options per type
Section titled “Configuration options per type”Each field carries a configuration object whose available keys depend on the
field type (the schema editor shows only what applies). All types accept a
description (help text). The rest:
| Field type | Option | What it does |
|---|---|---|
text |
minLength / maxLength |
Length bounds |
pattern |
A regular expression the value must match, e.g. ^[A-Z]{2}-\d{4}$ |
|
textarea |
minLength / maxLength |
Length bounds |
richtext |
maxLength |
Size cap |
integer, number, currency, percent |
minimum / maximum |
Inclusive value bounds (for currency, applied to the amount) |
number |
multipleOf |
Step size, e.g. 0.01 for two decimal places |
percent |
precision |
Decimal places |
currency |
currencyCode |
ISO 4217 code (USD, EUR, GBP) |
boolean |
trueLabel / falseLabel |
Display labels, e.g. “Active” / “Inactive” |
date |
minDate / maxDate |
Earliest/latest allowed date |
datetime |
minDatetime / maxDatetime |
Earliest/latest allowed timestamp |
time |
minTime / maxTime |
Earliest/latest time of day (HH:MM) |
phone |
phoneFormat |
international, national, or any |
defaultCountry |
ISO country code for parsing/display | |
url |
allowedProtocols |
e.g. ["https"] |
picklist, multipicklist |
options |
The selectable options, in display order — each with a value, label, colour, default flag, active flag, and lifecycle category (see Lifecycles) |
multipicklist |
minSelections / maxSelections |
How many options must/may be chosen |
reference |
displayField |
Which field of the target record to show as the link’s label |
address |
requiredFields |
Which address parts must be filled (street, city, state, postalCode, country) |
defaultCountry |
Pre-selected country | |
attachment |
allowedMimeTypes |
e.g. ["image/*", "application/pdf"] |
maxFileSizeMB |
Per-file size cap (default 10) | |
maxFiles |
How many files the field holds (default 1) | |
formula |
formula |
The expression and its declared result type |
lookup |
lookup |
The reference field to follow (via) and the target field to project |
rollup |
rollup |
Source table, reference field, aggregate (count/sum/min/max/avg), optional value field and filter |
autonumber |
autonumber |
Display format (e.g. INV-{0000}, with {YYYY}/{YY}/{MM} from the creation date) and the first number issued. The start is fixed at creation; the format can change any time — already-issued numbers are never renumbered. |
Every rule in this table is enforced at every write surface — the app, the
REST API, CSV imports, and AI/MCP writes — and a field’s default value must
satisfy its own rules. Records written before a rule was added (or before the
date/time bounds, allowedProtocols and phoneFormat rules began enforcing in
August 2026) keep their stored values; the rules apply when a record is next
written.
Settings every field has
Section titled “Settings every field has”- Required — the field must have a value on every record.
- Unique — no two records may share a value. Not available on compound types (address, geolocation, opening hours, attachment) or computed types.
- Default value — pre-filled on new records, and applied to any record created without the field (API, import, flows). Date and datetime fields can default dynamically to Today / Now instead of a fixed value: the app prefills your local date, and a write that omits the field gets the creation date on the server (UTC). A reference to Users can default to Current user: the form is prefilled with you, and a record created without the field — through the API, an import, a flow or an AI agent — is stamped with whoever created it. Use it for “rated by”, “assigned to” and “owner” fields so people stop picking themselves from a list.
- Searchable — whether the field’s text feeds global search (on by default; only meaningful for text-like types; API-only for now — see Search).
- Description and AI hint — for humans and AI assistants respectively.
- Display order — position in grids and forms.
Changing a field: what happens to existing data
Section titled “Changing a field: what happens to existing data”Fields are edited with PUT /tables/{tableId}/fields/{fieldId}. The rules
engine classifies every change:
- Loosening is always allowed. Dropping a pattern, raising a maximum, removing required/unique, adding picklist options — nothing already stored can be invalidated, so nothing is checked.
- Tightening is verified first. Adding required or unique, narrowing a range, removing a picklist option: the server scans the existing rows before changing anything, and if any row would violate the new rule the change is refused with up to five example row IDs so you can fix the data and retry. Record writes to the table are briefly paused while the check runs, so a racing edit cannot slip past it; on a very large table the check can time out — nothing is changed, and the error says to retry in a quiet period.
- Type changes never rewrite stored values. Types that share a storage shape can be swapped in place: the text-like group (text, textarea, richtext, email, phone, url, picklist) interconvert, as does the decimal group (number, currency, percent). Existing values are re-validated against the new type’s rules like any tightening. Any other pair — e.g. text → integer, or anything involving boolean, dates, references, or compound types — is refused unless the table is empty, in which case the column is simply swapped. A field can never change between a computed type and a regular one: delete and recreate instead.
- Dependants block risky changes. A type change is refused while lookups or rollups elsewhere read the field; an incompatible change is refused while sibling formulas or table constraints use it; unique cannot be removed from a field other tables use as their reference label; and a picklist option that a stage machine names cannot be removed or renamed while the machine points at it. Each refusal names exactly what depends on the field.
The internal field name, like the table name, is fixed at creation — only the display name changes.
Deleting a field
Section titled “Deleting a field”DELETE /tables/{tableId}/fields/{fieldId} removes the field from the
schema: it disappears from every record, form, and export, and any role
grants on it are removed with it. Deletion is refused while computed fields
elsewhere read the field, or while a stage rule requires it to enter a stage
— resolve those first. As with tables, there is no undo from the app.
Relationships between tables
Section titled “Relationships between tables”A reference field links each record to at most one record in another table — the “many” side of a one-to-many relationship (each deal points at one company; a company can have many deals).
Creating one, you choose:
- The target — another table in the workspace, or the built-in Users or Groups system entities (e.g. an “Account manager” field pointing at a workspace user).
- The label field (table targets) — the field on the target table shown as the link’s text in grids and pickers. It must be unique on the target table, so every link is unambiguous. This is also why removing unique from a field is blocked while references use it as their label.
Referential integrity is enforced by the database: you cannot point a reference at a record that does not exist, and deleting a record that other records still reference is blocked until those references are cleared. See Data consistency for the full story, including composite constraints and cross-table automation rules.
Once a relationship exists, it powers more than navigation:
- The record detail page lists the referencing records as child records.
- Lookup fields project a value across the reference (a deal shows its company’s industry).
- Rollup fields aggregate the other way (a company shows the sum of its open deals). Both are covered in Formulas, rollups & summaries.
Table constraints
Section titled “Table constraints”Beyond per-field required/unique, a table can carry table-level
constraints. Open a table’s schema page and use the Constraints section
(requires fields:manage), or manage them through the API
(GET/POST /tables/{tableId}/constraints,
PUT/DELETE /tables/{tableId}/constraints/{constraintId}) and the AI schema
assistant. Three kinds exist:
- Composite unique — a combination of 2–5 fields must be jointly unique
(e.g. one price row per product and region). Add an optional
condition to make it a conditional unique: the rule then applies only
to the rows the condition selects, so a single field is allowed. “Each rater
has at most one favourite” is the
Raterfield with the condition[Is favourite] = true; “one primary contact per company” isCompanywith[Is primary] = true; “one active subscription per customer” isCustomerwith[Status] = 'active'. Rows where the condition is false or empty are exempt. The condition uses the same Excel-familiar syntax as formula fields and is compiled server-side, never raw SQL. - No overlap — rows sharing the same scope values may not have overlapping date or datetime ranges (e.g. one booking per room per night). You name the start and end fields (both dates, or both datetimes) and choose whether the end is inclusive.
- Check — every row must satisfy a condition over its own fields
(e.g.
[Check out] > [Check in],[Min guests] <= [Max guests]). A row where any operand is empty passes.
All three are enforced by the database itself, so no write path can bypass them. Adding one is a tightening change: it fails with the offending row IDs if existing data already violates it, and editing one replaces the rule in place under the same id — if existing rows would break the new rule, the old rule stays in force.
Field visibility: hiding a column from some roles
Section titled “Field visibility: hiding a column from some roles”Each field has a visibility setting, shown as a chip in the schema editor’s fields grid:
- Workspace (default) — anyone who can see the record sees the field.
- Private — the field is hidden (and rejected on write) except for admins and roles that hold an explicit grant.
Toggling the chip calls PUT /tables/{tableId}/fields/{fieldId}/visibility
and requires permissions:manage — if the chip is not clickable, that is
why. Grants are per role, per field: PUT /roles/{roleId}/fields/{fieldId}
sets canRead and/or canUpdate for a role on a private field (also
permissions:manage; in the app: Workspace Settings → Roles → field
grants). A private field is masked server-side everywhere — lists,
details, exports, search — and admins always see all fields. The full
model, including record-level visibility, is in
Authorization.
The AI schema assistant
Section titled “The AI schema assistant”You do not have to build a schema field by field. The Data page hosts an
AI schema assistant that designs tables, fields, options, and constraints
conversationally and proposes them as a validated plan; applying a plan
requires the admin-only schema:apply action, and plans are strictly
additive — they can create tables and fields but never delete, rename, or
retype anything. See AI assistants.
Q: Why can’t I change my text field to a number field?
Because the table has data and the two types store values differently — converting would mean rewriting (and possibly mangling) stored values, which the system never does. Your options: pick a compatible type from the same group, empty the table first, or create a new field and migrate values manually (an import with update mode works well).
Q: I made a field required and the request failed with a list of row IDs.
That is the pre-check working: those rows have no value in the field. Fill them in (or decide the field should not be required) and retry. Nothing was changed by the failed attempt.
Q: Who can see the schema itself?
Everyone in the workspace — tables:read is in every built-in role. Field
values can be hidden per role; field definitions cannot.
Q: Can I rename a table or field’s internal name?
No. Display names change freely; internal names are permanent identifiers used by formulas, queries, imports, and the API. Choose short, stable, snake_case names at creation time.
Q: Why is the Visibility chip not clickable for me?
Toggling visibility requires permissions:manage, which only admins hold by
default — editing fields (fields:manage) is not enough, because hiding a
column is a permissions decision, not a schema one.
Q: Is there a limit on tables or rows?
Yes — per-organization limits on the number of tables and rows per table apply, depending on your plan. See Organization admin and Billing.