Sequences
Build and control multi-step outreach sequences (cadences). A sequence is an ordered series of steps — emails, delays, tasks, and LinkedIn actions — that contacts move through automatically.
Use the action endpoints, not raw CRUD
Create, rename, clone, and delete sequences with the dedicated action endpoints below — not the generic POST / PATCH / DELETE /api/sequences verbs. Creating a sequence also provisions a backing source list, a start step, and a default view; deleting one cascades to its steps, list, and enrollments. The raw verbs skip this scaffolding and leave a sequence that cannot enroll contacts. (PATCH /api/sequences/{sequenceId} is still fine for editing settings like the sending mailbox — see that endpoint for the fields to avoid.)
Lifecycle
A sequence's status moves draft → active → paused. Use /activate and /pause to transition it — they also move in-flight contacts — rather than patching status directly.
Steps
Steps are managed with the regular /api/sequenceSteps CRUD endpoints. Each step has a type (sendEmail, delay, addTask, sendLinkedinMessage, sendLinkedinConnectionRequest, startAutomation, endSequence) and an order. The startSequence step (order 0) is created for you and should not be removed.
Returns sequences in a workspace. Filter with the where parameter, e.g. {"workspaceId": "<WORKSPACE_UUID>"} or {"workspaceId": "<WORKSPACE_UUID>", "status": "active"}.
The listId field on each sequence is its source list — add that list to a contact's listIds to enroll the contact (see the Sequence Enrollments section).
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/sequences 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",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
],
"total": 1
}Creates a draft sequence and provisions everything it needs: a backing source list (returned as listId), a default startSequence step, and a default view.
Use this endpoint instead of the generic POST /api/sequences — the generic verb creates a bare row with no source list, which cannot enroll contacts.
The new sequence starts in draft. Add steps with POST /api/sequenceSteps, then call POST /api/sequences/activate to start it.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Sequence name. Defaults to "New Sequence". Also used as the source list's name.
IANA timezone for the start step's sending window (e.g. "America/New_York"). Invalid values fall back to "UTC".
Optional sort order among sequences.
Sequence created
No workspace found
Authentication failed or token is invalid/expired
POST /api/sequences/create HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 53
{
"workspaceId": "workspace-uuid",
"name": "Q3 Outbound"
}{
"success": true,
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Retrieve a single sequence by ID. If no sequence matches the ID this returns 200 with data: null (not a 404) — the generic CRUD route doesn't 404. The custom action endpoints (activate, pause, etc.) are the ones that return 404 for a missing sequence.
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,domainSuccessful response
Authentication failed or token is invalid/expired
GET /api/sequences/{sequenceId} 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",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Update editable settings on a sequence, such as the default sending mailboxId, linkedinAccountId, icon, color, or settings.
Do not change
nameorstatushere. UsePOST /api/sequences/renameto rename (it keeps the source list name in sync) andPOST /api/sequences/activate/POST /api/sequences/pauseto change status (they also transition in-flight contacts).
If no sequence matches the ID this is a no-op and returns 200 with an empty result (not a 404).
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Editable sequence settings. To rename use POST /api/sequences/rename; to change status use /activate or /pause.
Default sending mailbox for email steps.
Sequence updated
Partial sequence object containing only the updated fields.
Authentication failed or token is invalid/expired
PATCH /api/sequences/{sequenceId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 28
{
"mailboxId": "mailbox-uuid"
}{
"data": {},
"sideEffects": []
}Sets a draft or paused sequence to active and begins processing enrolled contacts. Resuming from paused re-arms contacts that were paused mid-flight.
Validation runs first: the sequence must have at least one step after the start step, and every sendEmail step must have a sending mailbox (step-level or the sequence's mailboxId). If validation fails the endpoint returns 400 with a message.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Sequence activated (or already active)
Validation failed (no action step, or an email step has no mailbox)
Authentication failed or token is invalid/expired
The requested resource was not found
POST /api/sequences/activate HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 30
{
"sequenceId": "sequence-uuid"
}{
"success": true,
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Sets an active sequence to paused and snapshots every in-flight contact so it can resume from the same point when the sequence is re-activated. A sequence already in draft or paused is returned unchanged.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Sequence paused (or already paused/draft)
Present and true when in-flight contacts were paused.
Authentication failed or token is invalid/expired
The requested resource was not found
POST /api/sequences/pause HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 30
{
"sequenceId": "sequence-uuid"
}{
"success": true,
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
},
"pausedContacts": true
}Renames the sequence and its source list together, keeping them in sync. Prefer this over patching name directly.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Sequence renamed
Authentication failed or token is invalid/expired
The requested resource was not found
POST /api/sequences/rename HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 58
{
"sequenceId": "sequence-uuid",
"name": "Q3 Outbound (EMEA)"
}{
"success": true
}Duplicates an existing sequence into a new draft sequence with a fresh, empty source list. Steps and sequence-scoped email templates are copied (obsolete AI Composer steps are omitted; replyToStepId references are remapped to the new steps). Enrolled contacts are not copied.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Name for the copy. Defaults to "
Sequence cloned
Authentication failed or token is invalid/expired
The requested resource was not found
POST /api/sequences/clone HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 36
{
"sourceSequenceId": "sequence-uuid"
}{
"success": true,
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"listId": "123e4567-e89b-12d3-a456-426614174000",
"mailboxId": "123e4567-e89b-12d3-a456-426614174000",
"linkedinAccountId": "123e4567-e89b-12d3-a456-426614174000",
"sequenceFolderId": "123e4567-e89b-12d3-a456-426614174000",
"icon": "text",
"color": "text",
"order": 1,
"settings": {},
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
}Permanently deletes a sequence and cascades to its steps, source list and views, email templates, enrollments (SequenceContactStatus rows), and any scheduled emails. This is not reversible. Use this instead of the generic DELETE /api/sequences/{id}, which would orphan the related records.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Sequence deleted
Authentication failed or token is invalid/expired
The requested resource was not found
POST /api/sequences/delete HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 30
{
"sequenceId": "sequence-uuid"
}{
"success": true
}Returns the steps of a sequence. Filter with where, e.g. {"workspaceId": "<WORKSPACE_UUID>", "sequenceId": "<SEQUENCE_UUID>"}, and order by order: orderBy={"order":"asc"}.
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/sequenceSteps HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"sequenceId": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"type": "startSequence",
"name": "text",
"subject": "text",
"content": {},
"settings": {},
"triggerSettings": {},
"templateId": "123e4567-e89b-12d3-a456-426614174000",
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
],
"total": 1
}Adds a step to a sequence. Set type to one of sendEmail, delay, addTask, sendLinkedinMessage, sendLinkedinConnectionRequest, startAutomation, or endSequence, and order to position it after the start step (which is order 0).
Per-step configuration lives in the freeform settings and triggerSettings objects, and the fields differ by type. The reference below lists the commonly-used fields for each type.
Applies to every type:
settings.backgroundPhasesis managed by the server — don't set it; whatever you send is overwritten.Text fields on email, LinkedIn, and task steps support macros such as
{{contact.firstName}},{{contact.email}},{{contact.company.name}}, and{{mailbox.name}}.You don't create the
startSequencestep (order 0) — it ships with the sequence, but you canPATCHit to tune scheduling (see below). AI preparation happens in Drafts. Enable it withsettings.ai.enabled = trueon a send step.
sendEmail
settings.mailboxId
uuid
Sending mailbox. Falls back to the sequence's mailboxId; one of the two is required to activate.
settings.subject
string
Subject line (macros supported).
settings.content
Tiptap doc
Email body (macros supported).
settings.draft
boolean
true creates a draft for a user to send manually; false (or omitted) sends automatically.
settings.replyToStepId
uuid
Send as a reply within an earlier sendEmail step's thread. Must reference an earlier email step in the same sequence.
settings.stopOnReply
boolean
End the sequence for a contact who replies.
settings.ai
object
Set {"enabled": true, "mode": "full"} to AI-compose the email in Drafts.
The recipient is always the contact's email, and from defaults to the mailbox identity.
{
"settings": {
"mailboxId": "<MAILBOX_UUID>",
"subject": "Quick question, {{contact.firstName}}",
"content": {"type": "doc", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Hi {{contact.firstName}}, wanted to reach out."}]}]},
"draft": false
}
}delay
Waits before the next step runs. Configured via triggerSettings, not settings.
triggerSettings.delayMs
number
Wait, in milliseconds. Defaults to 86400000 (24h) if omitted.
addTask
settings.title
string
Task title (macros supported).
settings.description
Tiptap doc / string
Task body.
settings.taskType
string
e.g. call, todo, meeting, follow_up.
settings.priority
integer
1 Low to 4 Urgent.
settings.assignedToIds
uuid[]
Assignee user ids. Defaults to the sequence creator.
settings.continueMode
string
When the sequence advances: immediate, done (default), or deadline.
settings.stopOnCallOutcomes
string[]
For call tasks, stop on selected outcomes when marked done: positive, negative, noAnswer. Evaluated before continuation, including the final step.
settings.deadlineDays
number
Days after creation for the deadline.
settings.deadlineTime
string
HH:MM for the deadline (default 09:00).
settings.deadlineTimezone
string
IANA timezone for the deadline.
sendLinkedinMessage and sendLinkedinConnectionRequest
settings.userId
uuid
Required. The workspace user whose connected LinkedIn account sends.
settings.content
Tiptap doc
Message body / connection note (macros supported). Connection notes are truncated to 300 characters.
settings.skipIfNoLinkedinData
boolean
Both types — skip the contact (instead of erroring) when they don't have a complete LinkedIn profile.
settings.skipIfNotConnected
boolean
Messages only — skip contacts who aren't 1st-degree connections instead of erroring.
settings.acceptedAction
string
Connection requests: continue (default) or endSequence, on acceptance or when already connected.
settings.notAcceptedAction
string
Connection requests: continue, endSequence, or wait indefinitely. Overrides legacy timeout settings.
settings.notAcceptedDays
integer
Connection requests: 1–90 days after sending; required for explicit continue or endSequence on non-acceptance.
settings.stopIfNotAccepted
boolean
Connection requests only — end the sequence if the request isn't accepted within settings.stopIfNotAcceptedDays days. Requires stopIfNotAcceptedDays to be set.
settings.stopIfNotAcceptedDays
number
Connection requests only — days to wait before ending the sequence when stopIfNotAccepted is on. 0 or omitted means no timeout is scheduled (the sequence won't end on non-acceptance).
startAutomation
settings.automationId
uuid
Required. The manual-trigger automation to run for the contact.
endSequence
Takes no settings — it marks the contact complete.
startSequence (PATCH only)
Created with the sequence; PATCH /api/sequenceSteps/{stepId} to adjust enrollment and scheduling.
settings.rollMode
string
auto (default) enrolls contacts automatically; manual holds new enrollments in waitingForApproval until approved in the app.
settings.dailyStarts
number
Max contacts that may start the sequence per day.
settings.activeDays
string[]
Days sends are allowed, e.g. ["mon", "tue", "wed", "thu", "fri"].
settings.sendingWindow
object
{"start": "09:00", "end": "17:00", "timezone": "<IANA>"}.
settings.staggerMinutes
number
Minimum minutes between auto-sent emails.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Email subject (for sendEmail steps).
Step body — a Tiptap document for sendEmail steps.
Per-type configuration. The fields depend on type — see the endpoint description for the full per-type reference (e.g. mailboxId/subject/content for sendEmail, userId for LinkedIn steps, automationId for startAutomation).
Timing configuration. Used by delay steps (delayMs); most other types leave this empty. See the endpoint description.
Position after the start step (which is order 0).
Step created
Authentication failed or token is invalid/expired
POST /api/sequenceSteps HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 147
{
"workspaceId": "workspace-uuid",
"sequenceId": "sequence-uuid",
"type": "delay",
"name": "Wait 2 days",
"order": 1,
"triggerSettings": {
"delayMs": 172800000
}
}{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"sequenceId": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"type": "startSequence",
"name": "text",
"subject": "text",
"content": {},
"settings": {},
"triggerSettings": {},
"templateId": "123e4567-e89b-12d3-a456-426614174000",
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
},
"sideEffects": []
}Updates multiple steps in one request — typically to reorder them. The body is an object keyed by step ID, where each value is the partial update for that step.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Steps updated
Authentication failed or token is invalid/expired
PATCH /api/sequenceSteps HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 53
{
"step-uuid-1": {
"order": 1
},
"step-uuid-2": {
"order": 2
}
}{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"sequenceId": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"type": "startSequence",
"name": "text",
"subject": "text",
"content": {},
"settings": {},
"triggerSettings": {},
"templateId": "123e4567-e89b-12d3-a456-426614174000",
"order": 1,
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000",
"updatedById": "123e4567-e89b-12d3-a456-426614174000"
}
],
"sideEffects": []
}Delete a sequence step. Use archive=true for a recoverable soft delete. The startSequence step should not be deleted.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
If true, soft deletes (archives) the step instead of permanently deleting it.
falseStep deleted
Hard delete (archive omitted or false): returns 1 on success.
Soft delete (archive=true): returns a single-element array containing the archived step object.
1Authentication failed or token is invalid/expired
DELETE /api/sequenceSteps/{stepId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": 1,
"sideEffects": []
}Update a single sequence step.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Changing a step's type is allowed but uncommon. The auto-managed startSequence type is intentionally excluded (matching SequenceStepCreate).
Step updated
Partial step object containing only the updated fields.
Authentication failed or token is invalid/expired
PATCH /api/sequenceSteps/{stepId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 49
{
"name": "Intro email",
"subject": "Quick question"
}{
"data": {},
"sideEffects": []
}Last updated

