View management

Saved views — the columns, sort, filters, layout, and grouping persisted per workspace and record root, plus the fork-on-edit default-view model.

A saved view is the persisted configuration of a records table: which columns show, how rows sort, which filters apply, the layout type, and any grouping. It is one row per (workspace, root) pair, addressed under the owning workspace.

Where POST /v1/records/query is transient (it returns rows for a one-off filter/sort and writes nothing), a view is persisted — it is the base configuration a grid opens with, and the thing the UI's column / sort / layout / group controls write to.

What a view holds

FieldTypeWhat it persists
columnsarrayColumn set + order + per-column config (width, pin).
sortarrayPersisted sort — { field, direction } entries.
filtersarrayPersisted filter rules (the typed FilterConfig model — see Filtering & sorting).
layout_typeenumThe view type: table · kanban · calendar · gallery · chart · list · graph. Defaults to table.
layout_configobjectLayout-specific config (per layout_type).
display_fieldsarrayFields surfaced by non-table layouts (list/calendar/kanban cards).
group_byarray · nullThe grouping field path — a discrete field for kanban, a date field for calendar; null for other layouts. Optional at create: when omitted on a kanban/calendar view it is auto-resolved and persisted (see Default grouping).
is_defaultbooleanWhether this is the default view for its root. Exactly one per (workspace, root).

Layout types are eligibility-gated by root: kanban needs a discrete (enum / text) field to group by, calendar needs a date field — both stored in group_by. A root with neither cannot select that layout. When you omit group_by, the server fills a logical default so the view renders grouped immediately — see Default grouping.

Endpoints

All view operations are scoped under the workspace that owns them.

MethodPathOperation
GET/v1/workspaces/{workspace_id}/viewsList the workspace's saved views.
POST/v1/workspaces/{workspace_id}/viewsCreate a view.
PATCH/v1/workspaces/{workspace_id}/views/{view_id}Update a view's fields.
DELETE/v1/workspaces/{workspace_id}/views/{view_id}Delete a view.

See the Records views & statistics group in the sidebar for the full request/response schema of each.

Trigger any view type

Every view possibility is reachable from the create / update body — set layout_type (one of the seven) plus group_by where the layout needs it. For example, a kanban board grouped by status:

POST /v1/workspaces/{workspace_id}/views
{
  "data": {
    "type": "workspace-view",
    "attributes": {
      "name": "Pipeline by status",
      "root": "companies",
      "layout_type": "kanban",
      "group_by": ["status"],
      "columns": [{ "field": "name" }, { "field": "status" }],
      "sort": [{ "field": "created_at", "direction": "desc" }]
    }
  }
}

Swap layout_type for calendar with a date group_by (e.g. ["due_date"]), or list / graph / table with no group_by, to trigger the other layouts. The same attributes are accepted by PATCH to reconfigure an existing view.

Default grouping (auto_group)

A kanban or calendar view needs a group_by to render grouped; without one it shows a "select a field" state. You don't have to supply it — when group_by is omitted on a kanban/calendar create (or a layout switch to one), the server resolves a logical default and persists it onto the view.

auto_group (boolean, default true) controls this. It is a request-only control flag and is not stored on the view. Pass auto_group: false to leave group_by unset (the view then renders the "select a field" state). It has no effect when you pass an explicit group_by (including explicit null), or for layouts that don't group (table / list / graph). On PATCH, auto-resolution applies only when switching a non-default view to kanban/calendar that doesn't already group — it never overwrites an existing group_by, and it never applies to a default view (which rejects group_by changes; see below).

Resolution is deterministic first, model-assisted only when needed:

TierPicks
1 · Curated presetA per-root default (e.g. invoicesstatus for kanban, due_date for calendar), used only when that field actually exists for the root.
2 · Sole candidateThe single eligible field — an enum for kanban, a date for calendar — when the root exposes exactly one.
3 · Model pickWhen several eligible fields exist and no preset applies, a model selects the most meaningful one. The choice is persisted, so it resolves once per view, not per request.

Auto-default only chooses from the enum (kanban) or date (calendar) candidates — it never auto-groups by free text. A root with no eligible field leaves group_by as null (the "select a field" state). The full set of eligible fields stays available for explicit selection: pass any eligible field as group_by to override the default.

The default view is a shared baseline

Exactly one view per (workspace, root) carries is_default: true. The default is a shared baseline across every user of the workspace, so its personal-state fields are protected: a PATCH to a default view that changes filters, sort, or group_by is rejected with 409 DEFAULT_VIEW_READONLY (these are exactly the fields the chat updateDataView tool would otherwise leak into the shared baseline).

Its layout fields stay mutable on the default — PATCH may still change layout_type, layout_config, display_fields, columns, name, and the is_default toggle itself.

To persist a filter, sort, or grouping, do not PATCH the default — create a new view (a fork) with those attributes. POST-ing them is always allowed; only mutating them on an existing default is blocked.

To make the fork the active view, promote it with PATCH … { "data": { "attributes": { "is_default": true } } }, which atomically demotes the previous default (the single-default invariant is enforced server-side). There is no /views/{id}/set-default action endpoint — is_default flows through the resource's PATCH like any other field (per the API Shape Standard: state transitions belong on the resource's existing PATCH, not a custom verb).

The web app automates this as fork-on-edit: the first time you change a filter/sort/group on a default grid, it creates a fork and promotes it for you. That convenience is client-side — at the API level a default simply rejects filters/sort/group_by mutations, and you fork explicitly.

How a view relates to a query

A view supplies the base columns, sort, and filters a grid opens with. POST /v1/records/query then runs against that root — you can pass a whereClause / orderBy to overlay a transient filter or sort on top of the view's base without persisting anything. To make a filter or sort stick, write it to the view via PATCH.

Persists to the viewTransient (no view write)
Filter / sortUI controls · PATCH /views · chat /layout-style slash submenusPOST /v1/records/query · MCP well_query_records · free-text chat
Layout / groupUI controls · PATCH /views · chat /layout · /group slash submenus— (no transient form; layout and group are properties of a view)

Layout and group exist only as persisted view properties — the read-only query surfaces (/records/query, MCP, free-text chat) have no layout/group parameter. Only a surface that writes the view can change them.