Views
Manage views within a list. Each view belongs to a list and defines its own filters, display type, and column configuration.
View types
grid— flat table layout. Columns are defined insettings.columns.board— kanban-style layout. Requiressettings.groupByto be set to the field to group by (e.g."stage"or"custom.<column-id>"). To create a board view, create it asgridfirst, then PATCH with{"type": "board"}.
Settings object
{
"columns": [
{ "key": "&" },
{ "key": "value" },
{ "key": "closeDate" },
{ "key": "custom.<column-id>" }
],
"groupBy": "stage",
"isDefault": true
}columns— ordered list of visible columns."&"is always the primary name column and should be first.groupBy— field key to group rows by (board views require this; grid views support it optionally).isDefault— marks this as the default view opened when navigating to the list. Only one view per list should haveisDefault: true.
Default columns
The API has no concept of default columns. When a view is created without a settings.columns array, it defaults to settings: {} with no columns visible. The UI populates sensible defaults client-side, but these are not enforced or provided by the API. Always include a columns array when creating views via the API.
Filters
Each view has its own filters object. For dynamic lists, this is what determines which records appear. Filters use the same operator syntax as the where query parameter. Field keys map directly to entity field names.
Patching filters replaces the entire filters object — always include all desired filters, not just the changed ones.
Returns views. Filter by list using {"listId": "<LIST_UUID>"} or by workspace using {"workspaceId": "<WORKSPACE_UUID>"}.
Each view has its own filters object that determines which records are shown. For dynamic lists, this is the primary mechanism for filtering records.
Filter syntax
View filters use the same operator syntax as the where parameter across the API. Field keys map to entity field names:
{
"name": { "$contains": "acme" },
"lastActivity.time": { "$lt": "-7d" },
"title": { "$contains": "Engineer" },
"custom.<column-id>": true
}All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Comma-separated list of fields to return. Defaults to all fields.
Supports relation expansion with dot-notation: a path like company.name embeds the named field of the related record, and quotes.id embeds the id of each related quote. Use .* to embed full related objects, e.g. quotes.*. Related records are returned nested on the response — as an object for to-one relations (e.g. company) or an array for to-many relations (e.g. quotes). Archived related records are excluded.
Example: GET /api/deals/{dealId}?fields=id,name,quotes.id returns the deal with a quotes array of {"id": "<QUOTE_UUID>"} entries.
id,name,domainJSON-encoded filter object. All top-level conditions are combined with AND logic. Use $or for OR logic.
The available operators depend on the data type of the field you are filtering on.
String fields
Fields like name, domain, description, linkedin, source, externalId, location.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"name": "Linear"} |
$eq | Explicit exact match | {"name": {"$eq": "Linear"}} |
$not | Not equal | {"source": {"$not": "import"}} |
$in | Matches any value in array | {"domain": {"$in": ["linear.app", "granola.so"]}} |
$notIn | Matches none of the values | {"source": {"$notIn": ["import", "api"]}} |
$contains | Case-insensitive word-boundary substring match | {"name": {"$contains": "YC"}} |
$notContains | Does not contain | {"name": {"$notContains": "Test"}} |
$containsAny | Contains any of the given strings | {"name": {"$containsAny": ["YC", "Techstars"]}} |
$startsWith | Starts with prefix | {"domain": {"$startsWith": "app."}} |
$endsWith | Ends with suffix | {"email": {"$endsWith": "@zero.inc"}} |
$exists | Field is present and truthy | {"linkedin": {"$exists": true}} |
$notExists | Field is absent, null, or empty | {"linkedin": {"$notExists": true}} |
Number fields
Fields like value, confidence.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"value": 5000} |
$eq | Explicit exact match | {"value": {"$eq": 5000}} |
$not | Not equal | {"value": {"$not": 0}} |
$gt | Greater than | {"value": {"$gt": 10000}} |
$gte | Greater than or equal | {"value": {"$gte": 5000}} |
$lt | Less than | {"value": {"$lt": 10000}} |
$lte | Less than or equal | {"value": {"$lte": 50000}} |
$in | Matches any value in array | {"confidence": {"$in": [0.25, 0.5, 0.75]}} |
$notIn | Matches none of the values | {"confidence": {"$notIn": [0, 1]}} |
$exists | Field is present and truthy | {"value": {"$exists": true}} |
$notExists | Field is absent, null, or zero | {"value": {"$notExists": true}} |
Multiple operators can be combined on one field:
{"value": {"$gte": 5000, "$lt": 10000}}
Date fields
Fields like closeDate, startDate, endDate, createdAt, updatedAt.
Values can be ISO 8601 strings ("2026-01-01", "2026-01-01T00:00:00Z") or relative time macros.
Relative time macros: +Nd / -Nd (days), +Nw / -Nw (weeks), +Nm / -Nm (months), +Ny / -Ny (years), +Nh / -Nh (hours), +Ns / -Ns (seconds), now().
| Operator | Description | Example |
|---|---|---|
$gte | On or after | {"closeDate": {"$gte": "2026-01-01"}} |
$lte | On or before | {"closeDate": {"$lte": "2026-03-31"}} |
$gt | After | {"createdAt": {"$gt": "2026-01-01T00:00:00Z"}} |
$lt | Before | {"createdAt": {"$lt": "now()"}} |
$date | Exact date match (compares date portion only) | {"closeDate": {"$date": "2026-01-15"}} |
$exists | Field is present and truthy | {"closeDate": {"$exists": true}} |
$notExists | Field is absent or null | {"closeDate": {"$notExists": true}} |
Date range example:
{"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"}}
Relative date example (closing in next 30 days):
{"closeDate": {"$gte": "now()", "$lte": "+30d"}}
Array fields
Fields like listIds, ownerIds, contactIds.
| Operator | Description | Example |
|---|---|---|
$includes | Array contains the given value (use this — bare exact match is not supported) | {"listIds": {"$includes": "<LIST_UUID>"}} |
$notIncludes | Array does not contain the given value | {"ownerIds": {"$notIncludes": "<USER_UUID>"}} |
$overlaps | Array contains at least one of the given values | {"ownerIds": {"$overlaps": ["<UUID_1>", "<UUID_2>"]}} |
$notOverlaps | Array contains none of the given values | {"listIds": {"$notOverlaps": ["<UUID_1>", "<UUID_2>"]}} |
$all | Array contains all of the given values | {"listIds": {"$all": ["<UUID_1>", "<UUID_2>"]}} |
$length | Filter by array length (supports nested operators) | {"contactIds": {"$length": {"$gte": 2}}} |
$exists | Field is present and non-empty | {"ownerIds": {"$exists": true}} |
$notExists | Field is absent or empty | {"ownerIds": {"$notExists": true}} |
UUID / ID fields
Fields like id, workspaceId, companyId, stage.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"stage": "<PIPELINE_STAGE_UUID>"} |
$in | Matches any value in array | {"stage": {"$in": ["<UUID_1>", "<UUID_2>"]}} |
$notIn | Matches none of the values | {"stage": {"$notIn": ["<UUID_1>"]}} |
$not | Not equal | {"stage": {"$not": "<UUID>"}} |
Boolean fields
Fields like archived.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"archived": false} |
$not | Not equal | {"archived": {"$not": true}} |
Logical operators
These work across all data types.
$or — matches if any sub-condition is true:
{
"workspaceId": "<WORKSPACE_UUID>",
"$or": [
{"ownerIds": {"$overlaps": ["<USER_UUID_1>", "<USER_UUID_2>"]}},
{"closeDate": {"$gte": "2026-01-01"}}
]
}
$and — matches if all sub-conditions are true (useful when you need multiple conditions on the same field):
{
"$and": [
{"name": {"$contains": "Enterprise"}},
{"name": {"$notContains": "Test"}}
]
}
Dot-notation (relation filtering)
Use dot-syntax to filter records based on properties of related objects:
{"company.name": "Linear"}
{"company.domain": {"$in": ["linear.app", "granola.so"]}}
{"company.location.city": "San Francisco"}
{"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]}}
All operators available for the target field's data type can be used with dot-notation.
Custom property filtering
Custom properties are stored in the custom object on records, keyed by UUID. Use GET /api/columns to discover the available custom property IDs, types, and options for a workspace.
Filter on custom properties using custom.<COLUMN_ID> with operators appropriate for the column's type.
Note: For
selectandmultiselectcolumns, option values are UUIDs (thekeyfield from the column'soptionsarray). Use the UUID, not the human-readable name.
// Text custom property
{"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}}
// Select custom property (use the option key UUID)
{"custom.a1b2c3d4-e5f6-7890-abcd-ef1234567890": "3e839b5c-b311-4887-b2da-727d2d75cdd6"}
// Multi-select custom property (use option key UUIDs)
{"custom.b2c3d4e5-f6a7-8901-bcde-f12345678901": {"$overlaps": ["a1b1c1d1-e1f1-1111-aaaa-111111111111", "b2b2c2d2-e2f2-2222-bbbb-222222222222"]}}
// Currency/number custom property
{"custom.c3d4e5f6-a7b8-9012-cdef-123456789012": {"$gte": 100000}}
// Date custom property
{"custom.d4e5f6a7-b8c9-0123-defa-234567890123": {"$gte": "2026-01-01"}}
// Boolean custom property
{"custom.e5f6a7b8-c9d0-1234-efab-345678901234": true}
// Check if custom property has a value
{"custom.f6a7b8c9-d0e1-2345-fabc-456789012345": {"$exists": true}}
Complex example
All top-level keys are ANDed together:
{
"name": {"$contains": "Zero"},
"location.city": "Helsinki",
"location.country": {"$in": ["United Kingdom", "Germany", "Sweden"]},
"value": {"$gte": 10000},
"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"},
"ownerIds": {"$includes": "<USER_UUID>"},
"stage": {"$in": ["<UUID_1>", "<UUID_2>"]},
"lastActivity": {"$exists": true},
"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]},
"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}
}
{"stage":"<PIPELINE_STAGE_UUID>"}Maximum number of records to return
100Pagination offset
0JSON string for sort order
{"name":"asc"}Successful response
Authentication failed or token is invalid/expired
GET /api/views HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"entity": "contacts",
"type": "grid",
"filters": {},
"settings": {},
"highlights": [],
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"total": 1
}Create a new view within a list. Both entity and type are required.
Supported type values: grid, board.
Board views: Create as
gridfirst, then PATCH{"type": "board"}. Board views should havesettings.groupByset — the API does not enforce it but the UI needs it to display columns correctly.
Default view: Set
settings.isDefault: trueon the first view, or apply it via a follow-up PATCH. Only one view per list should be the default.
Filters can be set at creation or added later via PATCH. For dynamic lists, filters on the view determine which records are shown.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
View type. Use grid for table layout, board for kanban.
Note: you can also POST directly with type: "board" — the API accepts it. The create-as-grid-then-PATCH approach also works.
Initial filters for the view. For dynamic lists, this controls which records are shown.
Initial display settings. If omitted, defaults to {} (no columns configured).
Should include columns array and optionally groupBy and isDefault.
{
"columns": [{ "key": "&" }, { "key": "value" }],
"groupBy": null,
"isDefault": true
}
Display order of this view within the list. Use 0 for the first/default view.
View created successfully
Authentication failed or token is invalid/expired
POST /api/views HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 191
{
"workspaceId": "workspace-uuid",
"listId": "list-uuid",
"name": "Active contacts",
"entity": "contacts",
"type": "grid",
"filters": {
"title": {
"$contains": "Engineer"
},
"lastActivity.time": {
"$lt": "-7d"
}
}
}{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"entity": "contacts",
"type": "grid",
"filters": {},
"settings": {},
"highlights": [],
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
"sideEffects": []
}Retrieve a single view by ID.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Successful response
Authentication failed or token is invalid/expired
GET /api/views/{viewId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"entity": "contacts",
"type": "grid",
"filters": {},
"settings": {},
"highlights": [],
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
}Permanently delete a view. Returns {"data": 1} on success, {"data": 0} if not found.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Deletion result
1 if deleted, 0 if not found
Authentication failed or token is invalid/expired
DELETE /api/views/{viewId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": 1,
"sideEffects": []
}Update a view's name, type, filters, or display settings. Returns only changed fields.
entity is immutable and cannot be changed after creation.
Patching filters replaces the entire filters object — include all desired filters, not just the changed ones.
Patching settings replaces the entire settings object — always include the full columns array and groupBy value, not just the changed fields.
To convert a grid view to a board view: PATCH {"type": "board"}. Board views should have settings.groupBy set — the API does not enforce it but the UI needs it to display columns correctly.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
All fields are optional. Multiple fields can be updated in a single PATCH request.
Display name of the view.
Change the view layout. Use "board" for kanban, "grid" for table.
Can be toggled freely — both grid→board and board→grid work.
When switching to "board", ensure settings.groupBy is set for meaningful column grouping.
Replaces the entire filters object — include all desired filters, not just the changed ones.
For dynamic lists this controls which records are shown.
Set to {} to clear all filters.
Replaces the entire settings object — always include the full columns array, groupBy, and isDefault values.
Partial settings updates will overwrite existing column configuration.
If settings is omitted on creation, it defaults to {}.
Display order of this view within the list.
View updated. Only changed fields are returned in data.
Authentication failed or token is invalid/expired
PATCH /api/views/{viewId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 81
{
"filters": {
"title": {
"$contains": "Engineer"
},
"lastActivity.time": {
"$lt": "-14d"
}
}
}{
"data": {},
"sideEffects": []
}Last updated

