Skip to content

Views, boards & sheets

Your records live in tables, but you rarely want to look at them the same way twice. Tessule gives you several surfaces over the same data: the table page for browsing and filtering one table, a board for working a pipeline lane by lane, Sheets for fast spreadsheet-style editing across every table, and SQL views for read-only derived tables an admin defines with a query.

If you only remember one thing:

Every surface is a different arrangement of the same records. They all read and write through the same records API, so the same row visibility, field masking, validation rules, stage rules, and approval rules apply on all of them — and a change made anywhere appears everywhere else live. A board or a sheet never lets you see or do anything the record itself wouldn’t.

Table page filter · sort · edit dialog Board stage lanes · drag cards Sheet inline cell editing The records API — one gate for every surface rows scoped · restricted fields masked · validation, stage & approval rules enforced Workspace tables read / write SQL views derived, read-only
Boards, sheets and the table page are arrangements of the same records, behind the same permission gate. SQL views appear alongside tables but are computed from a query and cannot be edited.

The surfaces themselves need no special permission — what you can do on them is governed by the ordinary record and table actions from the roles model:

Action Governs Held by default
records:read Seeing records on any surface Reader, Member, Admin
records:create Adding rows (dialog, sheet draft row) Member, Admin
records:update Editing cells, dragging board cards Member, Admin
records:delete Deleting rows Member, Admin
tables:manage Creating, editing, deleting SQL views Member, Admin
derivedViews:read Seeing computed columns and summary rows in Sheets Reader, Member, Admin
derivedViews:manage Workspace-shared computed columns / summary rows Member, Admin
query:execute Running the SQL a view is built from Reader, Member, Admin

Reading a SQL view’s rows has one extra requirement beyond an action: unrestricted record visibility (see SQL views below).


Every table has a records page (from the home page’s table list, or Data in the Build menu for builders). It’s a grid of all the table’s rows — there is no pagination, so the count in the header is the real total of what you can see.

What you can do there:

  • Search — the toolbar search box filters across all columns at once.
  • Sort — click a column header. Sorting is type-aware (numbers sort numerically, dates by date).
  • Filter — from a column’s menu or the toolbar’s filter control. Picklist and reference columns filter by choosing from their options rather than typing. Unique fields are not filterable, and the system id column is hidden by default (re-enable it from the column menu).
  • Show/hide columns — via the columns control in the toolbar. Compound fields are split into filterable sub-columns (an address becomes Street, City, State, Postal Code, Country; a geolocation becomes Lat and Lng).
  • Add / edit / deleteAdd Item and the per-row edit action open a form dialog with one input per field, pre-filled with the field defaults on create. Validation errors are listed at the top of the dialog and on the offending inputs; nothing is saved until they pass. Deleting asks for confirmation.
  • Export CSV — downloads exactly the rows and fields shown to you. It is built client-side from data you already have, so it can never include a record or field your permissions hide.
  • Open — the leftmost action opens the full record page for comments, history, sharing, and attachments (see Records).

If a save is intercepted by an approval rule, the dialog closes and you’re offered a request approval flow instead — the record is unchanged until an approver accepts it. See Approvals.

An arrangement you want back tomorrow can be saved as a named view — a remembered filter, sort, column set and row height — from the view picker in the toolbar. Pick one to apply it; the URL carries ?view=<id>, so a filtered list is a link you can send someone. The link carries the view’s identity, never its contents: whoever opens it still sees only the rows and fields their own permissions allow.

A view has an audience, chosen when you save it:

Audience Who sees it Who can write it
Only me Just you — nobody else can see or open it You
A group you’re in That group’s members customization:manage
Everyone in the workspace Anyone who can read the table customization:manage

Marking a view as the default makes it the one that surface opens with, scoped the same way: your own default overrides your group’s, which overrides the workspace’s. A group and the workspace can each hold a default per view kind, so Sales and Finance can open the same table differently without arguing about it.

Which group’s arrangement applies when you’re in more than one is your own choice, made under Page layout in the app bar — see Page layout: your group’s arrangement.

A saved view isn’t only a spreadsheet grid. Each view can be drawn four ways, chosen by the view’s kind (a toggle bar above the data):

  • Grid — the default table. Every view saved before this setting existed stays a grid unchanged.
  • Gallery — one card per record, with a heading, an optional image from an attachment field, and a handful of fields under the title.
  • Calendar — records placed on a month grid by a date/datetime field (or the record’s own createdAt/updatedAt).
  • Map — records as markers, placed by a geolocation field.

The kind changes only how the rows are drawn, never which rows: every kind draws the same records the grid would, under the same filter, sort, and permissions — a calendar is a grid whose rows are placed on a date, a map a grid whose rows are placed on a coordinate. It changes nothing about what the API returns, and a view can never hide data (see How permissions shape every surface).

Kinds that need a field to mean anything are only offered when the table has one — a map needs a geolocation field and is unavailable without one; a calendar can always fall back to the record’s createdAt/updatedAt. When you pick such a kind it starts on the first suitable field, which the settings dialog can change; it never re-picks silently afterwards. Records whose date or coordinate is empty simply aren’t placed, and the view says how many rather than dropping them silently.


A board lays one table out as a pipeline: one lane per stage, one card per record. It appears at /boards/<tableId>, and a Board button shows on the table page whenever the table has at least one stage field — a picklist designated as a lifecycle (see Lifecycles). A table with no stage field has no board; a table with several (say a phase and a priority) gets a Group by selector.

Lanes are the stage field’s active options, in their declared order. Lanes for options marked as end states (Ended — success / Ended — failure) start collapsed — closed columns are usually the biggest and least interesting. Every lane shows its card count, and any lane can be collapsed or expanded.

No record is ever dropped from a board. Records whose stage value doesn’t match an active option land in one of three catch-all lanes, which appear only when non-empty and are always collapsed by default:

Lane Contains
Archived stages Records still holding a retired option value
Unknown Records holding a value the option list never declared
No stage Records with no stage value set yet

You can drag a card out of a catch-all lane (that’s how a record leaves a retired stage), but never into one.

Dragging a card to another lane performs an ordinary record update — the same PATCH /tables/{tableId}/items/{itemId} any edit uses — setting the stage field to the lane’s value. Consequences of that:

  • Guarded transitions apply. If the field has a transition matrix, lanes you cannot move the card to are greyed out while you drag. The greying is advisory; the server is the authority, and a refused move snaps the card back with the list of stages you could have moved to. See Lifecycles for how transition rules are defined.
  • Computed stages cannot be dragged. A stage worked out from other fields shows a “Read-only” chip and its cards are plain links, not draggables.
  • Concurrent edits are detected. If someone else changed the record while it was on your screen, the move is refused rather than overwriting them, and the board refreshes.
  • Approval rules still apply. On a table with approval rules, a drag that matches one is held like any other edit — the card snaps back and the notice tells you the change needs approval.
  • Cards move optimistically: the card lands in the lane immediately and is put back if the server refuses. One move per card is in flight at a time.

Cards can also be moved with the keyboard (the drag handles are focusable), with screen-reader announcements as the card moves. Clicking a card opens its record page.

Boards are live: a card a teammate moves relocates on your screen without a refresh, via the same event stream that keeps every surface current.


Sheets (in the main navigation) is a full-screen, Excel-style view: every active table is a tab along the bottom, and the grid supports inline editing without opening a dialog. Rows are windowed, so large tables scroll smoothly.

  • Click a cell (or press Enter / F2) to edit in place; typing over a selected cell replaces its value. Enter commits and moves down, Tab commits and moves right, Esc cancels.
  • Delete/Backspace clears a cell; Space toggles a boolean.
  • Each commit saves that one field immediately — there is no “save” button. The status area in the bottom bar shows pending saves and the outcome.
  • Compound values that don’t fit a one-line editor (rich text, address, opening hours, attachments) open a small popup editor; geolocation edits inline as lat, lng.
  • A draft row at the bottom adds records: fill cells and the row is created once every required field has a value (defaults are pre-filled).
  • Ctrl+Z / Ctrl+Y undo and redo your own cell edits in this session. Undo writes the previous value back — it’s a new save, not a server-side rollback, so it still goes through validation and permissions.

Arrow keys move the selection; Ctrl+Arrow jumps to the edge; Home/End go to the row’s first/last column (Ctrl+Home/End to the grid’s corners); PageUp/PageDown move a screen at a time. The name box in the toolbar shows the selected record’s ID — the same durable handle the API uses — and can copy it.

Ctrl+C / Ctrl+X / Ctrl+V work on the selected cell, and paste understands multi-cell tab-separated data — so a block copied from Excel or Google Sheets pastes across rows and columns starting at the selected cell. Paste is forgiving by design: cells that fail validation, target a read-only column, or fall on rows/fields you cannot edit are skipped, and the status line reports Pasted N cells (M skipped). Pasting never creates rows beyond the existing ones.

Invalid input is refused at commit: the in-cell editor shows the field’s validation message and stays open, or (for background saves like paste) the cell pulses red and the message appears in the status line. A save the server refuses — validation, a permission, a guarded stage transition — is rolled back in the grid, so what you see always matches what was stored.

Clicking a cell you cannot edit tells you why in the status line rather than silently ignoring you: the field is computed (formula, lookup, rollup, a computed column), assigned automatically (autonumber), restricted by field-level permissions, or you lack records:update / records:create.

Column widths, hidden columns and row height (compact / standard / tall) are adjustable from the column headers and toolbar, and persist per browser — they are personal, not shared, and don’t follow you across devices. Tri-state sorting (click a header: ascending → descending → off) is applied client-side and never reorders anything for anyone else.

Sheets can also show computed columns and pinned summary rows (aggregates like sums and counts) — these are part of the derived-data feature set; see Derived data. And when two people land on the same cell, a presence outline shows who else is editing it.


A SQL view is a table whose rows are the result of a SELECT statement, recomputed on every read. Views appear in the tables list, in Sheets (with a VIEW badge), and to API and AI consumers — anywhere a table appears — but they are strictly read-only: adding, editing or deleting “rows” of a view is rejected, because its rows are query results with no stored identity.

Use one to publish a curated slice or join of your data — “open deals with their account names”, “this quarter’s renewals” — that members can browse like a table without writing SQL themselves.

  • Create one from the Query page (Save as view promotes the SELECT in the editor) or via POST /views — requires tables:manage.
  • Edit the definition or display name via the view’s edit dialog (tables list, or from the sheet toolbar when you’re looking at one) — PUT /views/{viewId}, same tables:manage action.
  • Delete a view the same way as a table — DELETE /tables/{tableId}. If other views are built on it, deletion (and definition changes) are refused with VIEW_HAS_DEPENDENTS until those are removed first.

Who can read a view’s rows: because a view’s SQL may join any table in the workspace, its rows are served only to people with unrestricted record visibility — the same rule as POST /query. If your role’s record scope is restricted on any private table, view rows are unavailable to you (the UI says so on the disabled control rather than hiding it). Row-level scoping is not applied inside a view — it’s all-or-nothing by design, so a view can never become a side door around a private table. Admins, who see all rows anyway, always qualify.


All four surfaces inherit the permission model described in Authorization, enforced on the server:

  • Rows outside your scope are simply absent — not greyed out. On a private table, the table page, board and sheet all show only the rows your scope and shares allow. Lane counts on a board are counts of what you can see.
  • Fields you can’t read don’t exist for you. Restricted fields are masked out of the data before it leaves the server, so their columns are hidden in sheets and grids, and exports can’t contain them.
  • Fields you can’t write are read-only cells, and the sheet tells you so when you try.
  • The UI hides or disables buttons you lack the action for, but that is a courtesy — every write is re-checked server-side.

Q: Can I save a filtered view of a table and share it with my team?

Not as a named, shareable configuration — filters, sorting and column choices on the table page and in Sheets are personal, session/browser-local settings. The shareable equivalent is a SQL view (an admin-defined derived table everyone with unrestricted visibility can read) or a saved query.

Q: My board is missing a column for a stage I know exists.

Empty catch-all lanes (Archived stages, Unknown, No stage) are hidden, and lanes for retired options only appear while records still hold the value. An active stage always has a lane, even when empty — if you don’t see it, check the Group by selector: the board may be grouped by a different stage field.

Q: Why did my card snap back when I dragged it?

The server refused the write. The notice tells you which: the stage’s transition rules don’t allow that move (it lists the allowed targets), someone else changed the record first, or an approval rule is holding the change. See Lifecycles and Approvals.

Q: Why can’t I edit anything in a sheet tab marked VIEW?

That tab is a SQL view — its rows are recomputed from a SELECT on every read, so there is nothing to edit. Change the view’s definition (needs tables:manage) to change what appears.

Q: I hid columns and resized them in Sheets, but my colleague sees the defaults.

Expected — sheet display settings are stored in your browser only. Schema changes (which fields exist, their order and visibility) are shared; how you lay them out is not.

Open the app