Skip to content

Workspace API

Workspace Views

Shared Dynamic and Static Views over Accounts, Contacts, and Projects — definition grammar, Favorites, and follow semantics.

A View is a named, workspace-shared scope over exactly one dataset (account, contact, or project). Dataset and type are immutable after creation. Names are trimmed, non-empty, and case-insensitively unique within a workspace and dataset.

TypeMembership
DynamicLive query. Stores versioned web filter state (q plus dataset-specific fields). Membership is re-evaluated against current data on every open or search.
StaticExplicit set of record IDs in workspace_view_records. Membership changes only through add/remove.

Definitions and Static membership are shared and editable by every workspace member. Favorites are personal: each member may favorite either type, order their Favorites, and use the resulting sidebar section. Favorites require a user-authenticated session (JWT); API keys cannot manage them.

In this API, follow means putting a record in a Static View (and, when useful, favoriting that View). It does not mean notifications, alerts, or activity subscriptions.

Contact visibility and Static follow

Ordinary POST /workspace/contacts/search remains book-only (workspace_contacts). Static Contact membership is an additional visibility source for web Contacts browse, Contact detail, and View execution: a Contact stays visible after its last project link or book overlay disappears until the last same-workspace Static Contact membership is removed. Adding the first membership still requires the Contact to already be visible; a candidate row cannot bootstrap access.

Removing a record from its last Static View does not delete an existing workspace overlay. Deleting an overlay does not remove Static membership.

Definition grammar is not SearchRequest

Dynamic definitions use the web table FilterState contract (version, q, fields), not the nested Workspace/Data API SearchRequest shape (query, filters, relations). Sending a SearchRequest-style body as definition returns 400 invalid_view_definition. It is not translated or partially accepted.

Valid Account Dynamic definition:

Valid Account definition
{
"version": 1,
"q": "architect",
"fields": {
"account_type": ["architect"],
"icp_tier": ["tier_1"],
"atlas_score": {
"min": "60"
}
}
}

Invalid — nested SearchRequest (rejected):

Invalid definition (SearchRequest shape)
{
"query": "architect",
"filters": {
"account_type": { "value": "architect" },
"icp_tier": { "value": "tier_1" }
}
}

Static definitions are only:

{ "version": 1 }

Membership lives in the records table, not in the definition.

v1 stored fields keys

DatasetStored fields keys
Accountaccount_type, city, atlas_score, icp_tier, last_activity
Contactrole, status, account_id, city
Projectstage, primary_use, city, sqft, units, detected

Project disposition is absent in v1: the web Projects table does not expose it, so stored View state cannot include a criterion the UI cannot show or edit.

Value shapes:

  • Enum and dynamic-enum fields: non-empty arrays of unique strings (OR within one field).
  • Entity fields (account_id): one-element UUID string arrays.
  • Number ranges (atlas_score, sqft, units): optional digit-string min / max.
  • Date ranges (last_activity, detected): optional YYYY-MM-DD after / before.
  • q is trimmed and ANDed with fields; different fields are ANDed.

Unknown fields, invalid enums, empty or duplicate values, malformed ranges, exact_match, exclude, and relational objects are rejected with invalid_view_definition.

Presentation

Both types may store versioned presentation:

{
"version": 1,
"sorting": [],
"column_visibility": {}
}

sorting uses TanStack { id, desc } column IDs. Presentation is web UI state. Public and MCP View-record searches ignore presentation sorting and use an explicit request sort or the dataset executor default.

Scopes and credits

SurfaceAuthCredits
List / get Viewsworkspace:read (API key or member JWT)0
Upsert / delete Views, add/remove membershipworkspace:write0
FavoritesUser JWT + membership only0
POST /workspace/views/{id}/records/searchSame as other Workspace searches1 per returned row